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]);

}
posted @ 2021-09-26 21:48  ·Iris  阅读(182)  评论(0编辑  收藏  举报