codeforces 979E(dp套dp)
题意:
有n个点,编号为1~n。有的点颜色是黑色,有的点颜色是白色,有的点的颜色待涂。你还可以连一些边,但这些边一定是从小编号连到大编号的点。
对于一个确定的图,我们去统计有多少条路径满足“该路径经过的所有点的颜色都是黑白相间的”,如果这种路径总数的奇偶性为p(p是输入的,p=0或1),那么该图就被称为好图
我们需要统计所有图中,好图的个数。答案对1e9+7取模。
分析:
我们先考虑对于一个给定的图,如何求出这种路径的总数
dp[i][0]表示点i是黑点,以点i为终点的所有路径的条数;dp[i][1]表示点i是白点,以点i为终点的所有路径的条数
那么只需要从前往后dp一下就能求出结果了
注意我们只关系奇偶性,所以是在模2意义下,那么dp值只有0和1两种取值
那么有一种显然的dp套dp的思路:f[i][S]表示已经做完了前i个点,前i个点的dp值是S情况下的图的个数
但是最多有50个点,状态是2^50,这样不能解决
仔细分析后发现,我们关心的并不是前i-1个点的具体dp值,我们关心的只是dp[j][0]=0、dp[j][0]=1、dp[j][1]=0、dp[j][1]=1的状态的分别的数目!
于是dp[i][ew][ow][eb][ob]表示前i个点,有ew个even-white点(即以某白点为终点的路径总数是偶数,后面同理),有ow个odd-white点,有eb个even-black点,有ob个odd-black点的情况下,图的个数
那么最后答案就是满足(ow+ob)%2==p的状态的图的个数的总和
这样复杂度是O(n^5),我们考虑优化一下
首先很明显,ob=i-ew-ow-eb,于是可以省掉一个维的状态,复杂度就变成O(n^4)了,可以通过
再进一步,我们发现偶点是随便连的,于是我们只需要记状态为dp[i][ow][ob]就行了,时间复杂度O(n^3)
1 #include<bits/stdc++.h> 2 using namespace std; 3 const int maxn=50,mod=1e9+7; 4 int dp[maxn+5][maxn+5][maxn+5][maxn+5]; 5 int c[maxn+5]; 6 int n,p; 7 int pw[maxn+5]; 8 void inc(int&a,int b) 9 { 10 a=(a+b)%mod; 11 } 12 int main() 13 { 14 scanf("%d%d",&n,&p); 15 for(int i=1;i<=n;++i) scanf("%d",&c[i]); 16 pw[0]=1; 17 for(int i=1;i<=maxn;++i) pw[i]=pw[i-1]*2LL%mod; 18 if(c[1]==0||c[1]==-1) dp[1][0][0][0]=1; 19 if(c[1]==1||c[1]==-1) dp[1][0][1][0]=1; 20 for(int i=2;i<=n;++i) 21 for(int ew=0;ew<=i;++ew) 22 for(int ow=0;ow<=i;++ow) 23 for(int eb=0;eb<=i;++eb) 24 { 25 if(ow+ew+eb>i) continue; 26 int ob=i-ew-ow-eb; 27 if(c[i]==1||c[i]==-1) 28 { 29 if(ow+ew!=0) 30 { 31 long long s=0; 32 if(ow>=1) 33 if(ob==0) s=dp[i-1][ew][ow-1][eb]; 34 else s=1LL*pw[ob-1]*dp[i-1][ew][ow-1][eb]%mod; 35 if(ew>=1) 36 if(ob>=1) s+=1LL*pw[ob-1]*dp[i-1][ew-1][ow][eb]%mod; 37 s%=mod; 38 s=s*pw[eb+ew+ow-1]%mod; 39 inc(dp[i][ew][ow][eb],(int)s); 40 } 41 } 42 if(c[i]==0||c[i]==-1) 43 { 44 if(ob+eb!=0){ 45 long long s=0; 46 if(ob>=1) 47 if(ow==0) s=dp[i-1][ew][ow][eb]; 48 else s=1LL*pw[ow-1]*dp[i-1][ew][ow][eb]%mod; 49 if(eb>=1) 50 if(ow>=1) s+=1LL*pw[ow-1]*dp[i-1][ew][ow][eb-1]%mod; 51 s%=mod; 52 s=s*pw[eb+ob+ew-1]%mod; 53 inc(dp[i][ew][ow][eb],(int)s); 54 } 55 } 56 } 57 int ans=0; 58 for(int ew=0;ew<=n;++ew) 59 for(int ow=0;ow<=n;++ow) 60 for(int eb=0;eb<=n;++eb) 61 if(ew+ow+eb<=n) 62 if((ow+n-ew-ow-eb)%2==p) 63 inc(ans,dp[n][ew][ow][eb]); 64 printf("%d\n",ans); 65 return 0; 66 }