当前位置: 首页 > 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.rkmt.cn/news/22662.html

相关文章:

  • winserver文件备份到minio
  • 教你把未分配的磁盘合并到C盘或者D盘?如何把未分配的硬盘空间分配到另一个磁盘?Windows 11,如何将未分配的磁盘分配给 C 盘?怎么把未分配的磁盘合并到d盘
  • 实用指南:VMware挂载Kail Linux
  • OpenCV基础操作与图像处理 - 指南
  • 2025年行业内游乐设施/过山车游乐设施权威榜单厂家-河北天鸿游乐设备
  • 机器学习技术助力美国西海岸地震预警系统升级
  • 2025年市场课桌椅/钢塑课桌椅最新TOP排名厂家-江西华聚智能家具集团有限公司
  • AT 随机做题 I
  • 画图3D真好用 - Gon
  • 高校与某中心共建机器人技术教育项目
  • WordPress维护模式完整指南:手动实现与插件方案
  • 原型链污染学习
  • 重新认识 Golang 中的 json 编解码
  • 关于价值原语与AI元人文构想的对话全记录——DeepSeek研究
  • Pytorch66页实验题
  • uni-app x开发商城系统,商品列表
  • PySimpleGUI 中有没有类似VB的timer组件
  • 向量空间与子空间
  • 西工大开源 Easy Turn:全双工轮次转换检测模型;百度 MuseSteamer 引入开放世界生成能力丨日报
  • 2025.10.16总结
  • containerd二进制安装
  • 维修笔记 | 一例滤波电容老化引发开关电源异常现象
  • (一)GPU与CUDA概述
  • 微软已停止对 Windows 10 系统的支持
  • 2023 ICPC Hefei
  • postgresql第一篇:postgresql收到一条sql语句后做了什么
  • Windows 事件ID + 登录类型 + 服务对应表大全
  • 10.16日学习笔记
  • 技术人不用当“兼职运营”:2025微信编辑器实用指南,让产品更新日志/API教程产出效率提升3倍
  • 10.16 —— 2021ccpc桂林D,B