当前位置: 首页 > news >正文

Luogu P10027 梦境世界 题解 [ 蓝 ] [ 多维 DP ]

梦境世界:可爱 DP 题。

一种常见的假做法是在 DP 的过程中记录路径的前驱进行转移,这种做法错误的原因并不在于转移存在环,它其实就是一张 DAG,但是这种状态表示方式并不能推导出前驱的前驱是谁,所以才是假的。

考虑正解。观察路径,发现路径一定由正常走、回溯走、正常走、回溯走的周期进行。因此我们可以在方格取数式的 DP 中给每个转移加一个系数,表示在进行这次移动之前,先进行了 \(x\) 次移动并且用 \(x\)回溯回到了原位。强制钦定回到原位的原因是因为这样才可以清晰地划分路径的状态。

剩下的就是简单的了,我们分两个 DP 做:

  • 先求出 \(g_{i, j, k}\),表示走到 \((i, j)\),在接下来的路线中先走 \(k\) 步,然后再回溯 \(k\) 步,回到 \((i, j)\) 的方案数。类似背包,利用乘法原理转移即可。注意需要倒着枚举 \(i, j\)
  • 再求出 \(f_{i, j, k}\),表示走到 \((i, j)\),已经用了 \(k\) 次回溯的方案数。转移和方格取数差不多,但是需要加上 \(g\) 的系数。

时间复杂度 \(O(n^4)\),如果被卡常可以考虑调换 DP 三个维度的顺序和取模卡常。

#include<bits/stdc++.h>
#define fi first
#define se second
#define eb(x) emplace_back(x)
#define pb(x) push_back(x)
#define lc(x) (tr[x].ls)
#define rc(x) (tr[x].rs)
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef long double ldb;
using pi = pair<int, int>;
const int N = 105;
int n, m, p, s, f[N][N][N], g[N][N][N], mod;
bitset<N> ban[N];
int main()
{//freopen("sample.in", "r", stdin);//freopen("sample.out", "w", stdout);ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);cin >> n >> m >> p >> mod >> s;while(s--){int x, y;cin >> x >> y;ban[x][y] = 1;}for(int lv = 0; lv <= p; lv++){for(int i = n; i >= 1; i--){for(int j = m; j >= 1; j--){if(lv == 0){if(ban[i][j] == 0) g[i][j][0] = 1;continue;}if(i + 1 <= n && ban[i + 1][j] == 0){for(int c = 0; c < lv; c++){g[i][j][lv] += (1ll * g[i][j][lv - c - 1] * g[i + 1][j][c]) % mod;if(g[i][j][lv] >= mod) g[i][j][lv] -= mod;}}                    if(j + 1 <= m && ban[i][j + 1] == 0){for(int c = 0; c < lv; c++){g[i][j][lv] += (1ll * g[i][j][lv - c - 1] * g[i][j + 1][c]) % mod;if(g[i][j][lv] >= mod) g[i][j][lv] -= mod;}}}}}f[1][1][0] = 1;for(int lv = 0; lv <= p; lv++){for(int i = 1; i <= n; i++){for(int j = 1; j <= m; j++){if(ban[i][j]) continue;if(i + 1 <= n && ban[i + 1][j] == 0){for(int c = 0; c + lv <= p; c++){f[i + 1][j][c + lv] += (1ll * f[i][j][lv] * g[i][j][c]) % mod;if(f[i + 1][j][c + lv] >= mod) f[i + 1][j][c + lv] -= mod;}}if(j + 1 <= m && ban[i][j + 1] == 0){for(int c = 0; c + lv <= p; c++){f[i][j + 1][c + lv] += (1ll * f[i][j][lv] * g[i][j][c]) % mod;if(f[i][j + 1][c + lv] >= mod) f[i][j + 1][c + lv] -= mod;}}}}}int ans = 0;for(int i = 0; i <= p; i++) ans = (ans + f[n][m][i]) % mod;cout << ans;return 0;
}
http://www.hskmm.com/?act=detail&tid=32728

相关文章:

  • winserver文件备份到minio
  • 特殊函数
  • 教你把未分配的磁盘合并到C盘或者D盘?如何把未分配的硬盘空间分配到另一个磁盘?Windows 11,如何将未分配的磁盘分配给 C 盘?怎么把未分配的磁盘合并到d盘
  • 项目ai拷打
  • 混合(ZR 二十联测 A + MX 炼石 ABC)
  • Qt项目作品在苹果macos上编译运行效果/视频监控系统/物联网平台等
  • 电脑硬盘中的文件怎么搜索?电脑文件搜索太慢怎么办?
  • 2025年靠谱的风机/离心风机/轴流风机生产企业排行榜-江苏中南鼓风机有限公司
  • 2025年行业内游乐设施/过山车游乐设施权威榜单厂家-河北天鸿游乐设备
  • 机器学习技术助力美国西海岸地震预警系统升级
  • 2025年口碑好的挤浆机/单螺旋挤浆机TOP品牌推荐厂家-滕州市建兴机械有限公司
  • 2025年市场课桌椅/钢塑课桌椅最新TOP排名厂家-江西华聚智能家具集团有限公司
  • 2025年口碑好的垃圾袋/医疗垃圾袋排名推荐生产厂家-厦门市万塑环保材料有限公司
  • 深入理解 PHP-FPM 的最佳配置
  • 【GitHub每日速递 251017】95k star,程序员专属!超全做饭指南,涵盖千道美食做法与进阶秘籍
  • 洛谷 P6715 [CCO 2018] Fun Palace (神秘DP)
  • AT 随机做题 I
  • moni 32
  • git 舍弃当前所有修改
  • 2025.10.17——1蓝
  • C# 使用 using 关键字间接实现只读局部变量的方法
  • 画图3D真好用 - Gon
  • 高校与某中心共建机器人技术教育项目
  • 2025年国际物流服务领域优质品牌最新推荐排行榜 —— 聚焦行业头部企业核心优势与选择参考
  • WordPress维护模式完整指南:手动实现与插件方案
  • Lean语言如何连接数学与编程
  • Github上文本切分相关的优秀项目
  • 微信机器人开发
  • 微信社群机器人开发
  • 《程序员修炼之道:从小工到专家》第三章读后感