[FJWC2020 Day1T1] 人生

題目描述
現在你有nn個點,每個點有黑色(00)或者白色(11)或者沒有顏色(1-1),現在你需要把所有沒有顏色的點染成黑色或者白色
你還需要添加一些不重複的有向邊(i,j)(i,j),要求1i<jn1\leq i<j\leq n,也就是說這些邊需要從編號小的走到編號大的
一般的,一條合法的路徑是指他經過的任意兩個點的顏色不同。特別的,一個點也算作一條路徑
問最後有多少種符合條件的圖,使得這個圖上有奇數條合法路徑,答案對998244353998244353取模

對於10%10\%的數據,n5n\leq 5
對於40%40\%的數據,n50n\leq 50
對於50%50\%的數據,n150n\leq 150
對於65%65\%的數據,n500n\leq 500
對於80%80\%的數據,n5000n\leq 5000
對於全部的數據 ,n2×105n\leq2\times 10^5


solution
首先考慮對於確定的圖,我們怎麼計算可不可行
我們用cnticnt_i表示以第ii個點爲結尾的路徑個數,那麼我們應該有這樣的轉移
cnti=j=1i1 cntj(coloricolorj,(j,i))+1cnt_i=\begin{matrix}\sum_{j=1}^{i-1}\end{matrix}\ cnt_j(color_i\neq color_j,\exist(j,i))+1
然後我們只需要判斷cnti\sum cnt_i的奇偶性即可
那麼我們發現我們記錄出這個東西有點冗餘,我們讓cnticnt_i表示以ii點爲結尾的路徑個數的奇偶性,那麼我們之前的轉移方程可以寫成
cnti=(xorsumj=1i1 cntj)xor1(coloricolorj,(j,i))cnt_i=\begin{matrix}(\operatorname{xorsum}_{j=1}^{i-1}\end{matrix}\ cnt_j) \operatorname{xor} 1(color_i\neq color_j,\exist(j,i))
其中xorsum\operatorname{xorsum}表示異或和

然後我們考慮進行dpdp,因爲題目中說所有邊都是從編號小的連向大的,所以我們用fi,j,x,yf_{i,j,x,y}表示前ii個點,cntcntxorsum\operatorname{xorsum}jj,有xxcnt=1cnt=1的白色點,有yycnt=1cnt=1的黑色點,的方案數,轉移以將這個點染成白色爲例,分兩類進行討論,一個是這個點的cnt=1cnt=1,轉移爲

f[i][j][x+1][y]+=f[i1][jxor1][x][y]×calc(y,0)×2xf[i][j][x+1][y]+=f[i-1][j\operatorname{xor} 1][x][y]\times \operatorname{calc}(y,0)\times 2^x

cnti=0cnt_i=0時,

f[i][j][x][y]+=f[i1][j][x][y]×calc(y,1)×2xf[i][j][x][y]+=f[i-1][j][x][y]\times\operatorname{calc}(y,1)\times 2^x

其中calc(y,opt)\operatorname{calc}(y,opt)表示在一個大小爲yy的集合中,奇偶性爲optopt的子集的方案數(包括∅)

暴力枚舉選了幾個,這個轉移就是O(n4)O(n^4)的,可以拿到4040

我們考慮calc(y,opt)\operatorname{calc}(y,opt)等於多少,我們發現在y1y\geq1他一定是2y12^{y-1},爲什麼呢?
yy爲奇數的時候,我們把它看成二進制,那麼任意一個奇數個數的集合一定對應一個大小爲偶數的集合即他的補集,所以奇數偶數各佔一半
yy爲偶數的時候,我們考慮分成1,n11,n-1兩部分,已知n1n-1中選出奇數和偶數的方案數是相同的,那麼如果選剩下的一個那麼就是n1n-1中選奇數的方案,不選就是n1n-1中選偶數的方案,所以奇數偶數數量還是相同的

考慮邊界條件,當y=0y=0時,00個數中選偶數的方案數爲1100個數中選奇數的方案爲00,注意特判就可以

那麼現在轉移就變成了O(n3)O(n^3)的,可以拿到5050

我們發現ii這一維可以滾動數組滾掉,空間變成平方,在開O2的情況下應該可以拿到6565

考慮進一步優化,我們把calc\operatorname{calc}的值帶進之前的轉移方程,變成

f[i][j][x+1][y]+=f[i1][jxor1][x][y]×2i1(2x=2iy)f[i][j][x+1][y]+=f[i-1][j\operatorname{xor} 1][x][y]\times 2^{i-1}(2^x=2^{i-y})
f[i][j][x][y]+=f[i1][j][x][y]×2i1f[i][j][x][y]+=f[i-1][j][x][y]\times 2^{i-1}

當然y=0y=0的時候需要特判

觀察這個轉移,我們發現x,yx,y已經基本無關了,唯一有影響的就是是否是00,所以我們可以變成f[i][j][0/1][0/1]f[i][j][0/1][0/1],分別表示目前是否有白點和黑點,那麼這樣複雜度就變成了O(n)O(n),可以通過

#include <bits/stdc++.h>
using namespace std;

# define Rep(i,a,b) for(int i=a;i<=b;i++)
# define _Rep(i,a,b) for(int i=a;i>=b;i--)
# define RepG(i,u) for(int i=head[u];~i;i=e[i].next)

typedef long long ll;

const int N=2e5+5;
const int mod=998244353;

template<typename T> void read(T &x){
   x=0;int f=1;
   char c=getchar();
   for(;!isdigit(c);c=getchar())if(c=='-')f=-1;
   for(;isdigit(c);c=getchar())x=(x<<1)+(x<<3)+c-'0';
    x*=f;
}

int n;
int a[N];
int f[N][2][2][2];
int fac[N];
int ans;

int main()
{
	freopen("life.in","r",stdin);
	freopen("life.out","w",stdout);
	read(n);
	Rep(i,1,n)read(a[i]);
	fac[0]=1;
	Rep(i,1,n)fac[i]=fac[i-1]*2%mod;
	f[0][0][0][0]=1;
	Rep(i,1,n)
		Rep(j,0,1)
			Rep(x,0,1)
				Rep(y,0,1){
					if(a[i]!=1){
						f[i][j^1][x|1][y]+=1ll*f[i-1][j][x][y]*(y?fac[i-2]:fac[i-1])%mod;
						f[i][j^1][x|1][y]%=mod;
						f[i][j][x][y]+=1ll*f[i-1][j][x][y]*(y?fac[i-2]:0)%mod;
						f[i][j][x][y]%=mod;
					}
					if(a[i]){
						f[i][j^1][x][y|1]+=1ll*f[i-1][j][x][y]*(x?fac[i-2]:fac[i-1])%mod;
						f[i][j^1][x][y|1]%=mod;
						f[i][j][x][y]+=1ll*f[i-1][j][x][y]*(x?fac[i-2]:0)%mod;
						f[i][j][x][y]%=mod;	
					}
				}
	Rep(i,0,1)
		Rep(j,0,1)
			ans+=f[n][1][i][j],ans%=mod;
	printf("%d\n",ans);
	return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章