zoukankan      html  css  js  c++  java
  • cf round 482E Kuro and Topological Parity

    题意:一个长度为$n$的序列,一些地方是$0$,一些地方是$1$,$-1$的地方你可以选择填$0$或者$1$,你可以选择连一些边$x->y$满足$x<y$

    请问有多少种填数并连边的方法,使得交错路的个数的奇偶性是$p$

    $n leq 50$

    5维dp,可以减一维

    原本的5维分别是:现在考虑的是第几个位置,当前有多少个点是x(0/1),并且以它为结尾的交错路的个数的奇偶性是r(0/1)。

    dp的值是方案数

    我们发现,假如我们现在要更新的dp值是$dp[i][j][k][t]$,并且我枚举到的状态是当前这个点是1,以它为结尾有奇数个交错路。

    那么1连向它的边不影响他的奇偶性,0连向它的边,是偶数个的也不影响它的奇偶性,

    只有奇数的0才会影响它的奇偶性,那么如果以它为结尾有偶数数个交错路,那么它要和前面奇数个奇数的0连边

    这个可以预处理组合数就可以了

    //Serene
    #include<algorithm>
    #include<iostream>
    #include<cstring>
    #include<cstdlib>
    #include<cstdio>
    #include<cmath>
    using namespace std;
    #define ll long long
    #define db double
    #define For(i,a,b) for(int i=(a);i<=(b);++i)
    #define Rep(i,a,b) for(int i=(a);i>=(b);--i)
    const int maxn=50+7;
    const ll mod=1e9+7;
    ll n,Px,col[maxn],mi[maxn],ans;
    ll dp[maxn][maxn][maxn][maxn],C[maxn][maxn],Cd[2][maxn];//black,0/1,white,0/1
    
    char cc; ll ff;
    template<typename T>void read(T& aa) {
    	aa=0;cc=getchar();ff=1;
    	while((cc<'0'||cc>'9')&&cc!='-') cc=getchar();
    	if(cc=='-') ff=-1,cc=getchar();
    	while(cc>='0'&&cc<='9') aa=aa*10+cc-'0',cc=getchar();
    	aa*=ff;
    }
    
    int main() {
    	read(n); read(Px);
    	For(i,1,n) read(col[i]);
    	C[0][0]=1;
    	For(i,1,n) {
    		C[i][0]=1;
    		For(j,1,i) C[i][j]=(C[i-1][j-1]+C[i-1][j])%mod;
    	}
    	For(i,0,n) {
    		For(j,0,i) Cd[j&1][i]+=C[i][j];
    		Cd[0][i]%=mod; Cd[1][i]%=mod;
    	}
    	mi[0]=1; For(i,1,n) mi[i]=mi[i-1]*2%mod;
    	dp[0][0][0][0]=1; ll r,x;
    	For(i,1,n) For(j,0,i) For(k,0,i-j) For(t,0,i-j-k) {
    		r=i-j-k-t;
    		if(col[i]!=1&&j+k>0) {//black,0/1
    			x=mi[j+k-1]*mi[t]%mod;
    			if(j) //black,0
    				dp[i][j][k][t]+=x*dp[i-1][j-1][k][t]%mod*Cd[1][r]%mod;
    			if(k) //black,1
    				dp[i][j][k][t]+=x*dp[i-1][j][k-1][t]%mod*Cd[0][r]%mod;
    		}
    		if(col[i]!=0&&t+r>0) {
    			x=mi[t+r-1]*mi[j]%mod;
    			if(t) //white,0
    				dp[i][j][k][t]+=x*dp[i-1][j][k][t-1]%mod*Cd[1][k]%mod;
    			if(r) //white,1
    				dp[i][j][k][t]+=x*dp[i-1][j][k][t]%mod*Cd[0][k]%mod;
    		}
    		dp[i][j][k][t]%=mod;
    		if(i==n&&((k+r)&1)==Px) ans+=dp[i][j][k][t];
    	}
    	printf("%lld
    ",ans%mod);
    	return 0;
    }
    

    ud20180701:

    今天才知道,这道题可以O(n)做……

    考场上看到这道题,第一反应是惊喜,哇,做过,第二反应是,诶,数据范围2e5???

    想了好久才想起当初怎么做的,印象很深自己当时是O(n^4)做的,还给Achen和NicoDafaGood讲过。

    然后思考了一下,发现白偶和黑偶是可以合并的,于是优化成了O(n^3),得了65分

    当时场上还发现了,如果当前是黑,前面含有白奇,那么转移到黑奇和黑偶的方案是相同的。

    但是没有想到,这两个转移的方案和就是2^i,所以转移到一个就是2^(i-1)。(i是前面的点数,不包括自己)

    然后就记录一下前面有没有白奇和黑奇,以及白奇的点数和黑奇的点数的奇偶性就可以了。

    O(n^3)代码:

    //Serene
    #include<algorithm>
    #include<iostream>
    #include<cstring>
    #include<cstdlib>
    #include<cstdio>
    #include<cmath>
    using namespace std;
    #define ll long long
    #define db double
    #define For(i,a,b) for(register int i=(a);i<=(b);++i)
    #define Rep(i,a,b) for(register int i=(a);i>=(b);--i)
    const int maxn=5000+7,maxt=507;
    const ll mod=998244353;
    ll n,d,a[maxn],mi[maxn],C[maxn][maxn];
    ll f[2][maxt][maxt],g[maxn][2];
    
    template<typename T>void read(T& aa) {
    	aa=0;char cc=getchar();T ff=1;
    	while((cc!='-')&&(cc<'0'||cc>'9')) cc=getchar();
    	if(cc=='-') ff=-1,cc=getchar();
    	while(cc>='0'&&cc<='9') aa=aa*10+cc-'0',cc=getchar();
    	aa*=ff;
    }
    
    ll solve() {//O(n^3)
    	mi[0]=1; For(i,1,n) mi[i]=mi[i-1]*2%mod;
    	C[0][0]=g[0][0]=1;
    	For(i,1,n) {
    		C[i][0]=g[i][0]=1;
    		For(j,1,i) {
    			C[i][j]=(C[i-1][j-1]+C[i-1][j])%mod;
    			g[i][j&1]+=C[i][j];
    		}
    		g[i][0]%=mod; g[i][1]%=mod;
    	}
    	int o=0,t; ll x,y; f[0][0][0]=1;
    	For(i,1,n) {
    		o^=1;
    		For(j,0,i) For(k,0,i-j) f[o][j][k]=0;
    		For(j,0,i-1) For(k,0,i-1-j) if(f[o^1][j][k]) {//j:white odd , k:black odd (1)
    			x=(f[o^1][j][k]%=mod); t=i-1-j-k;//t:even(black&white) (0)
    			if(a[i]!=1) {//white
    				y=x*mi[t+j]%mod;
    				f[o][j+1][k]+=y*g[k][0]%mod;//odd
    				f[o][j][k]+=y*g[k][1]%mod;  //even
    			}
    			if(a[i]!=0) {//black
    				y=x*mi[t+k]%mod;
    				f[o][j][k+1]+=y*g[j][0]%mod;//odd
    				f[o][j][k]+=y*g[j][1]%mod;  //even
    			}
    		}
    	}
    	ll rs=0;
    	For(i,0,n) For(j,0,n-i) if(f[o][i][j]) {
    		f[o][i][j]%=mod;
    		if(((i+j)&1)==d) rs+=f[o][i][j];
    	}
    	return rs%mod;
    }
    
    int main() {
    	freopen("graph.in","r",stdin);
    	freopen("graph.out","w",stdout);
    	read(n); read(d);
    	For(i,1,n) read(a[i]);
    	printf("%lld
    ",solve());
    //	cerr<<clock()<<"
    ";
    	return 0;
    }
    

    O(n)代码:

    //Serene
    #include<algorithm>
    #include<iostream>
    #include<cstring>
    #include<cstdlib>
    #include<cstdio>
    #include<cmath>
    using namespace std;
    #define ll long long
    #define db double
    #define For(i,a,b) for(int i=(a);i<=(b);++i)
    #define Rep(i,a,b) for(int i=(a);i>=(b);--i)
    const ll mod=998244353;
    const int maxn=1e6+7;
    ll n,d,a[maxn],dp[maxn][3][3],mi[maxn];
    
    template<typename T>void read(T& aa) {
    	aa=0;char cc=getchar();T ff=1;
    	while((cc!='-')&&(cc<'0'||cc>'9')) cc=getchar();
    	if(cc=='-') ff=-1,cc=getchar();
    	while(cc>='0'&&cc<='9') aa=aa*10+cc-'0',cc=getchar();
    	aa*=ff;
    }
    
    int main() {
    	freopen("graph.in","r",stdin);
    	freopen("graph.out","w",stdout);
    	read(n); read(d);
    	mi[0]=1; For(i,1,n) mi[i]=mi[i-1]*2%mod;
    	For(i,1,n) read(a[i]);
    	dp[0][0][0]=1;
    	For(i,0,n-1) For(j,0,2) For(k,0,2) if(dp[i][j][k]) {
    		dp[i][j][k]%=mod;
    		if(a[i+1]!=1) {//white
    			if(k) {
    				dp[i+1][j? 3-j:1][k]+=dp[i][j][k]*mi[i-1]%mod;
    				dp[i+1][j][k]+=dp[i][j][k]*mi[i-1]%mod;
    			}
    			else dp[i+1][j? 3-j:1][k]+=dp[i][j][k]*mi[i]%mod;
    		}
    		if(a[i+1]!=0) {//black
    			if(j) {
    				dp[i+1][j][k? 3-k:1]+=dp[i][j][k]*mi[i-1]%mod;
    				dp[i+1][j][k]+=dp[i][j][k]*mi[i-1]%mod;
    			}
    			else dp[i+1][j][k? 3-k:1]+=dp[i][j][k]*mi[i]%mod;
    		}
    	}
    	ll ans=0;
    	For(i,0,2) For(j,0,2) if(dp[n][i][j]&&((i+j)&1)==d) ans+=dp[n][i][j]%mod;
    	printf("%lld
    ",ans%mod);
    	return 0;
    }
    

      

  • 相关阅读:
    操作系统的运行机制
    操作系统的目标和功能
    操作系统的特征
    操作系统的基本概念
    error: a label can only be part of a statement and a declaration is not a statement
    克隆git仓库中的一个分支
    ubuntu下update-alternatives命令的使用
    堆排序
    合并排序
    递归算法的一些规则
  • 原文地址:https://www.cnblogs.com/Serene-shixinyi/p/9094435.html
Copyright © 2011-2022 走看看