6553. 【GDOI2020模擬4.11】人生

我才發現這篇寫完的博客一直在草稿箱裏躺着???


題目大意

數軸上排列着nn個點,點的顏色有黑白兩種,部分點已經確定顏色,部分點沒有確定。
每個點可以任意向右邊的點連邊,可以連可以不連。
求交錯路徑(相鄰的兩個點顏色互異)總數爲奇數的圖的方案數。
n200000n\leq 200000


思考歷程

早上在打SCOI2018,所以沒有做比賽。
下午的時候思考了一下,想到了個維護異或卷積前綴和來輔助轉移的方法。
後來發現跟正解完全對不上,原來是這個想法本來就考慮不周到。


正解

辣雞DP。
end(i)end(i)表示以ii結尾的路徑的條數。
首先可以搞出最簡單的狀態:fi,j,k,0/1f_{i,j,k,0/1},表示前ii個點,有jj個點爲白色並且endend爲奇數,有kk個點爲黑色並且endend爲偶數,路徑總數(即x=1iend(x)\sum_{x=1}^{i} end(x))是偶數還是奇數,這個狀態下的方案數。
轉移可以做到O(1)O(1):
fi,j,k,t×c(k,0)×2ikfi+1,j+1,k,t1f_{i,j,k,t}\times c(k,0) \times 2^{i-k} \to f_{i+1,j+1,k,t^1}
fi,j,k,t×c(k,1)×2ikfi+1,j,k,tf_{i,j,k,t}\times c(k,1) \times 2^{i-k} \to f_{i+1,j,k,t}
fi,j,k,t×c(j,0)×2ijfi+1,j,k+1,t1f_{i,j,k,t}\times c(j,0) \times 2^{i-j} \to f_{i+1,j,k+1,t^1}
fi,j,k,t×c(j,1)×2ijfi+1,j,k,tf_{i,j,k,t}\times c(j,1) \times 2^{i-j} \to f_{i+1,j,k,t}
其中c(n,0/1)c(n,0/1)表示nn個點中選擇偶數或奇數個點的方案數。
然後就可以O(n3)O(n^3)

接下來可以優化一下這條東西。
c(k,0)=Ck0+Ck2+Ck4+...=Ck10+Ck11+Ck12+...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=0k=0時,c(k,0)=1c(k,0)=1
k>0k>0時,c(k,0)=2k1c(k,0)=2^{k-1}
同理,c(k,1)=Ck1+Ck3+Ck5=Ck10+Ck11+Ck12+...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=0k=0時,c(k,1)=0c(k,1)=0
k>0k>0時,c(k,1)=2k1c(k,1)=2^{k-1}
於是可以優化DP狀態:fi,0/1,0/1,0/1f_{i,0/1,0/1,0/1},時間複雜度O(n)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,然後優化優化再優化。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章