6553. 【GDOI2020模拟4.11】人生

题目大意

数轴上排列着\(n\)个点,点的颜色有黑白两种,部分点已经确定颜色,部分点没有确定。
每个点可以任意向右边的点连边,可以连可以不连。
求交错路径(相邻的两个点颜色互异)总数为奇数的图的方案数。
\(n\leq 200000\)


思考历程

早上在打SCOI2018,所以没有做比赛。
下午的时候思考了一下,想到了个维护异或卷积前缀和来辅助转移的方法。
后来发现跟正解完全对不上,原来是这个想法本来就考虑不周到。


正解

辣鸡DP。
\(end(i)\)表示以\(i\)结尾的路径的条数。
首先可以搞出最简单的状态:\(f_{i,j,k,0/1}\),表示前\(i\)个点,有\(j\)个点为白色并且\(end\)为奇数,有\(k\)个点为黑色并且\(end\)为偶数,路径总数(即\(\sum_{x=1}^{i} end(x)\))是偶数还是奇数,这个状态下的方案数。
转移可以做到\(O(1)\):
\(f_{i,j,k,t}\times c(k,0) \times 2^{i-k} \to f_{i+1,j+1,k,t^1}\)
\(f_{i,j,k,t}\times c(k,1) \times 2^{i-k} \to f_{i+1,j,k,t}\)
\(f_{i,j,k,t}\times c(j,0) \times 2^{i-j} \to f_{i+1,j,k+1,t^1}\)
\(f_{i,j,k,t}\times c(j,1) \times 2^{i-j} \to f_{i+1,j,k,t}\)
其中\(c(n,0/1)\)表示\(n\)个点中选择偶数或奇数个点的方案数。
然后就可以\(O(n^3)\)

接下来可以优化一下这条东西。
\(c(k,0)=C_{k}^{0}+C_{k}^{2}+C_{k}^{4}+...=C_{k-1}^0+C_{k-1}^1+C_{k-1}^2+...\)
\(k=0\)时,\(c(k,0)=1\)
\(k>0\)时,\(c(k,0)=2^{k-1}\)
同理,\(c(k,1)=C_{k}^{1}+C_{k}^3+C_{k}^5=C_{k-1}^0+C_{k-1}^1+C_{k-1}^2+...\)
\(k=0\)时,\(c(k,1)=0\)
\(k>0\)时,\(c(k,1)=2^{k-1}\)
于是可以优化DP状态:\(f_{i,0/1,0/1,0/1}\),时间复杂度\(O(n)\)


代码

using namespace std;
#include <cstdio>
#include <cstring>
#include <algorithm>
#define N 200010
#define ll long long
#define mo 998244353
int n;
ll pow2[N];
int a[N];
int f[N][2][2][2];
inline void add(int &a,ll b){a=(a+b)%mo;}
int main(){
	freopen("life.in","r",stdin);
	freopen("life.out","w",stdout);
//	freopen("in.txt","r",stdin);
	scanf("%d",&n);
	pow2[0]=1;
	for (int i=1;i<=n;++i)
		pow2[i]=pow2[i-1]*2%mo;
	for (int i=1;i<=n;++i)
		scanf("%d",&a[i]);
	if (a[1]!=1) f[1][1][0][1]=1;
	if (a[1]!=0) f[1][0][1][1]=1;
	for (int i=1;i<n;++i)
		for (int j=0;j<2;++j)
			for (int k=0;k<2;++k)
				for (int t=0;t<2;++t){
					ll tmp=f[i][j][k][t];
					if (tmp==0)
						continue;
					if (a[i+1]!=1){
						add(f[i+1][1][k][t^1],tmp*(k?pow2[i-1]:pow2[i]));
						add(f[i+1][j][k][t],tmp*(k?pow2[i-1]:0));
					}
					if (a[i+1]!=0){
						add(f[i+1][j][1][t^1],tmp*(j?pow2[i-1]:pow2[i]));
						add(f[i+1][j][k][t],tmp*(j?pow2[i-1]:0));
					}
				}
	ll ans=f[n][0][0][1]+f[n][0][1][1]+f[n][1][0][1]+f[n][1][1][1];
	printf("%lld\n",ans%mo);
	return 0;
}

总结

DP问题的常见套路:先搞出个效率低下的DP,然后优化优化再优化。

posted @ 2020-04-11 20:49  jz_597  阅读(149)  评论(0编辑  收藏  举报