題目描述
現在你有個點,每個點有黑色()或者白色()或者沒有顏色(),現在你需要把所有沒有顏色的點染成黑色或者白色
你還需要添加一些不重複的有向邊,要求,也就是說這些邊需要從編號小的走到編號大的
一般的,一條合法的路徑是指他經過的任意兩個點的顏色不同。特別的,一個點也算作一條路徑
問最後有多少種符合條件的圖,使得這個圖上有奇數條合法路徑,答案對取模
對於的數據,
對於的數據,
對於的數據,
對於的數據,
對於的數據,
對於全部的數據 ,
solution
首先考慮對於確定的圖,我們怎麼計算可不可行
我們用表示以第個點爲結尾的路徑個數,那麼我們應該有這樣的轉移
然後我們只需要判斷的奇偶性即可
那麼我們發現我們記錄出這個東西有點冗餘,我們讓表示以點爲結尾的路徑個數的奇偶性,那麼我們之前的轉移方程可以寫成
其中表示異或和
然後我們考慮進行,因爲題目中說所有邊都是從編號小的連向大的,所以我們用表示前個點,的爲,有個的白色點,有個的黑色點,的方案數,轉移以將這個點染成白色爲例,分兩類進行討論,一個是這個點的,轉移爲
當時,
其中表示在一個大小爲的集合中,奇偶性爲的子集的方案數(包括∅)
暴力枚舉選了幾個,這個轉移就是的,可以拿到分
我們考慮等於多少,我們發現在他一定是,爲什麼呢?
當爲奇數的時候,我們把它看成二進制,那麼任意一個奇數個數的集合一定對應一個大小爲偶數的集合即他的補集,所以奇數偶數各佔一半
當爲偶數的時候,我們考慮分成兩部分,已知中選出奇數和偶數的方案數是相同的,那麼如果選剩下的一個那麼就是中選奇數的方案,不選就是中選偶數的方案,所以奇數偶數數量還是相同的
考慮邊界條件,當時,個數中選偶數的方案數爲,個數中選奇數的方案爲,注意特判就可以
那麼現在轉移就變成了的,可以拿到分
我們發現這一維可以滾動數組滾掉,空間變成平方,在開O2的情況下應該可以拿到分
考慮進一步優化,我們把的值帶進之前的轉移方程,變成
當然的時候需要特判
觀察這個轉移,我們發現已經基本無關了,唯一有影響的就是是否是,所以我們可以變成,分別表示目前是否有白點和黑點,那麼這樣複雜度就變成了,可以通過
#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;
}