CF1515E(连续段 dp)
找到了一道有趣的题。
题解里都是用的奇怪的线性 dp + 组合数的方法,或者奇妙 EGF。
这里学到一种新的 dp 思想。
应该是叫连续段 dp。
这种 dp,考虑的是排列或者操作的顺序,以这个东西为目标来进行 dp 的。
考虑到我们一般是对某个排列进行 dp,也就是说这时候我们关注的是顺序而不是其他关系,这也就导致了我们按照位置作为 dp 阶段现在不好使了,需要设计新的 dp。
设状态 \(dp[i][j]\) 表示前 i 个数,形成了 j 条连续段。注意这些连续段之间的距离是没有限制的。
转移一
表示新插入的数可以选择新建一个连续段。
\[dp[i + 1][j + 1] += dp[i][j] \times (j +1)
\]
转移二
新插入的数可以选择和原连续段接在一起
\[dp[i + 1][j] += dp[i][j] \times 2 \times j
\]
\[dp[i + 2][j] += dp[i][j] \times 2 \times j
\]
转移三
新插入的数可以用来合并两个连续段
\[dp[i + 3][j - 1] += dp[i][j] \times (j - 1)
\]
\[dp[i + 2][j - 1] += dp[i][j] \times (j - 1) \times 2
\]
如果会了这种 \(dp\) 思想,还是很好做的。
考虑 \(dp[i][j]\) 的本质是已经插入了 \(i\) 个数,形成了 \(j\) 条连续段。段内顺序不可变化,段与段之间允许插入新数,这个性质才能保证我们排列 \(dp\) 按顺序插入的正确性。
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define pii pair<int,int>
template<typename _T>
inline void read(_T &x)
{
x=0;char s=getchar();int f=1;
while(s<'0'||s>'9') {f=1;if(s=='-')f=-1;s=getchar();}
while('0'<=s&&s<='9'){x=(x<<3)+(x<<1)+s-'0';s=getchar();}
x*=f;
}
int n,Mod;
int dp[2333][2333];
// 你这么考虑问题,把他看成一个连续段 dp(我不太明白为什么叫组件 dp
// 这个 dp[i][j] 的实际意义是目前共计 i 个元素,构成了 j 个连续段,每两个连续段之间的距离可以看做无限
// 换一种理解方式,我们完全不关心这个排列的方案数,我们考虑的是每次操作序列方案数,我们本质是对这个玩意 dp
// 所以我们可以将每个数理解成一个物品,只考虑操作序列的 dp。dp 状态记录的东西需要和我们操作序列有关联
signed main()
{
read(n),read(Mod);
const int mod = Mod;
dp[1][1] = 1;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=i;j++)
{
dp[i + 1][j + 1] += (dp[i][j] * (j + 1)) % mod;
dp[i + 1][j + 1] -= dp[i + 1][j + 1] >= mod?mod:0;//mod;
// dp[i + 1][j + 1] -= dp[i + 1][j + 1] >= mod?mod:0;//mod;
dp[i + 1][j] += (dp[i][j] * 2 * j) % mod;
dp[i + 1][j] -= dp[i + 1][j] >= mod?mod:0;
// dp[i + 1][j] -= dp[i + 1][j] >= mod?mod:0;
dp[i + 2][j] += (dp[i][j] * 2 * j) % mod;
dp[i + 2][j] -= dp[i + 2][j] >= mod?mod:0;
// dp[i + 2][j] -= dp[i + 2][j] >= mod?mod:0;
// dp[i + 2][j] %= mod;
if(j >= 2)
{
dp[i + 3][j - 1] += (dp[i][j] * (j - 1)) % mod;
// dp[i + 3][j - 1] %= mod;
dp[i + 3][j - 1] -= dp[i + 3][j - 1] >= mod?mod:0;
// dp[i + 3][j - 1] -= dp[i + 3][j - 1] >= mod?mod:0;
dp[i + 2][j - 1] += (dp[i][j] * (j - 1) * 2) % mod;
dp[i + 2][j - 1] -= dp[i + 2][j - 1] >= mod?mod:0;
// dp[i + 2][j - 1] -= dp[i + 2][j - 1] >= mod?mod:0;
// dp[i + 2][j - 1] %= mod;
}
printf("dp[%lld][%lld] = %lld\n",i,j,dp[i][j]);
}
}
printf("%lld",dp[n][1]);
}