zoukankan      html  css  js  c++  java
  • CF1515E(连续段 dp)

    找到了一道有趣的题。

    题解里都是用的奇怪的线性 dp + 组合数的方法,或者奇妙 EGF。

    这里学到一种新的 dp 思想。

    应该是叫连续段 dp。

    这种 dp,考虑的是排列或者操作的顺序,以这个东西为目标来进行 dp 的。

    考虑到我们一般是对某个排列进行 dp,也就是说这时候我们关注的是顺序而不是其他关系,这也就导致了我们按照位置作为 dp 阶段现在不好使了,需要设计新的 dp。

    设状态 (dp[i][j]) 表示前 i 个数,形成了 j 条连续段。注意这些连续段之间的距离是没有限制的

    转移一

    表示新插入的数可以选择新建一个连续段。

    [dp[i + 1][j + 1] += dp[i][j] imes (j +1) ]

    转移二

    新插入的数可以选择和原连续段接在一起

    [dp[i + 1][j] += dp[i][j] imes 2 imes j ]

    [dp[i + 2][j] += dp[i][j] imes 2 imes j ]

    转移三

    新插入的数可以用来合并两个连续段

    [dp[i + 3][j - 1] += dp[i][j] imes (j - 1) ]

    [dp[i + 2][j - 1] += dp[i][j] imes (j - 1) imes 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
    ",i,j,dp[i][j]);
    		}
    
    	}
    	printf("%lld",dp[n][1]);
    
    }
    
  • 相关阅读:
    【Node.js】Jade视图模板的使用
    【Node.js】新建一个NodeJS 4.X项目
    【Node.Js】npm国内被墙的解决方法
    【AngularJS学习笔记】Java Script "use strict" 严格模式
    【Linux学习笔记】Linux-CentOS下安装Redis
    【Linux学习笔记】常用命令速记
    【AngularJS学习笔记】封装一些简单的控件(封装成Html标签)
    数组和切片1
    Go错误处理机制及自定义错误
    内置函数
  • 原文地址:https://www.cnblogs.com/-Iris-/p/15340292.html
Copyright © 2011-2022 走看看