题目描述
现在你有
n
n
n个点,每个点有黑色(
0
0
0)或者白色(
1
1
1)或者没有颜色(
−
1
-1
−1),现在你需要把所有没有颜色的点染成黑色或者白色
你还需要添加一些不重复的有向边
(
i
,
j
)
(i,j)
(i,j),要求
1
≤
i
<
j
≤
n
1\leq i<j\leq n
1≤i<j≤n,也就是说这些边需要从编号小的走到编号大的
一般的,一条合法的路径是指他经过的任意两个点的颜色不同。特别的,一个点也算作一条路径
问最后有多少种符合条件的图,使得这个图上有奇数条合法路径,答案对
998244353
998244353
998244353取模
对于
10
%
10\%
10%的数据,
n
≤
5
n\leq 5
n≤5
对于
40
%
40\%
40%的数据,
n
≤
50
n\leq 50
n≤50
对于
50
%
50\%
50%的数据,
n
≤
150
n\leq 150
n≤150
对于
65
%
65\%
65%的数据,
n
≤
500
n\leq 500
n≤500
对于
80
%
80\%
80%的数据,
n
≤
5000
n\leq 5000
n≤5000
对于全部的数据 ,
n
≤
2
×
1
0
5
n\leq2\times 10^5
n≤2×105
solution
首先考虑对于确定的图,我们怎么计算可不可行
我们用
c
n
t
i
cnt_i
cnti表示以第
i
i
i个点为结尾的路径个数,那么我们应该有这样的转移
c
n
t
i
=
∑
j
=
1
i
−
1
c
n
t
j
(
c
o
l
o
r
i
≠
c
o
l
o
r
j
,
∃
(
j
,
i
)
)
+
1
cnt_i=\begin{matrix}\sum_{j=1}^{i-1}\end{matrix}\ cnt_j(color_i\neq color_j,\exist(j,i))+1
cnti=∑j=1i−1 cntj(colori=colorj,∃(j,i))+1
然后我们只需要判断
∑
c
n
t
i
\sum cnt_i
∑cnti的奇偶性即可
那么我们发现我们记录出这个东西有点冗余,我们让
c
n
t
i
cnt_i
cnti表示以
i
i
i点为结尾的路径个数的奇偶性,那么我们之前的转移方程可以写成
c
n
t
i
=
(
xorsum
j
=
1
i
−
1
c
n
t
j
)
xor
1
(
c
o
l
o
r
i
≠
c
o
l
o
r
j
,
∃
(
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))
cnti=(xorsumj=1i−1 cntj)xor1(colori=colorj,∃(j,i))
其中
xorsum
\operatorname{xorsum}
xorsum表示异或和
然后我们考虑进行 d p dp dp,因为题目中说所有边都是从编号小的连向大的,所以我们用 f i , j , x , y f_{i,j,x,y} fi,j,x,y表示前 i i i个点, c n t cnt cnt的 xorsum \operatorname{xorsum} xorsum为 j j j,有 x x x个 c n t = 1 cnt=1 cnt=1的白色点,有 y y y个 c n t = 1 cnt=1 cnt=1的黑色点,的方案数,转移以将这个点染成白色为例,分两类进行讨论,一个是这个点的 c n t = 1 cnt=1 cnt=1,转移为
f [ i ] [ j ] [ x + 1 ] [ y ] + = f [ i − 1 ] [ j xor 1 ] [ x ] [ y ] × calc ( y , 0 ) × 2 x f[i][j][x+1][y]+=f[i-1][j\operatorname{xor} 1][x][y]\times \operatorname{calc}(y,0)\times 2^x f[i][j][x+1][y]+=f[i−1][jxor1][x][y]×calc(y,0)×2x
当 c n t i = 0 cnt_i=0 cnti=0时,
f [ i ] [ j ] [ x ] [ y ] + = f [ i − 1 ] [ j ] [ x ] [ y ] × calc ( y , 1 ) × 2 x f[i][j][x][y]+=f[i-1][j][x][y]\times\operatorname{calc}(y,1)\times 2^x f[i][j][x][y]+=f[i−1][j][x][y]×calc(y,1)×2x
其中 calc ( y , o p t ) \operatorname{calc}(y,opt) calc(y,opt)表示在一个大小为 y y y的集合中,奇偶性为 o p t opt opt的子集的方案数(包括∅)
暴力枚举选了几个,这个转移就是 O ( n 4 ) O(n^4) O(n4)的,可以拿到 40 40 40分
我们考虑
calc
(
y
,
o
p
t
)
\operatorname{calc}(y,opt)
calc(y,opt)等于多少,我们发现在
y
≥
1
y\geq1
y≥1他一定是
2
y
−
1
2^{y-1}
2y−1,为什么呢?
当
y
y
y为奇数的时候,我们把它看成二进制,那么任意一个奇数个数的集合一定对应一个大小为偶数的集合即他的补集,所以奇数偶数各占一半
当
y
y
y为偶数的时候,我们考虑分成
1
,
n
−
1
1,n-1
1,n−1两部分,已知
n
−
1
n-1
n−1中选出奇数和偶数的方案数是相同的,那么如果选剩下的一个那么就是
n
−
1
n-1
n−1中选奇数的方案,不选就是
n
−
1
n-1
n−1中选偶数的方案,所以奇数偶数数量还是相同的
考虑边界条件,当 y = 0 y=0 y=0时, 0 0 0个数中选偶数的方案数为 1 1 1, 0 0 0个数中选奇数的方案为 0 0 0,注意特判就可以
那么现在转移就变成了 O ( n 3 ) O(n^3) O(n3)的,可以拿到 50 50 50分
我们发现 i i i这一维可以滚动数组滚掉,空间变成平方,在开O2的情况下应该可以拿到 65 65 65分
考虑进一步优化,我们把 calc \operatorname{calc} calc的值带进之前的转移方程,变成
f
[
i
]
[
j
]
[
x
+
1
]
[
y
]
+
=
f
[
i
−
1
]
[
j
xor
1
]
[
x
]
[
y
]
×
2
i
−
1
(
2
x
=
2
i
−
y
)
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+1][y]+=f[i−1][jxor1][x][y]×2i−1(2x=2i−y)
f
[
i
]
[
j
]
[
x
]
[
y
]
+
=
f
[
i
−
1
]
[
j
]
[
x
]
[
y
]
×
2
i
−
1
f[i][j][x][y]+=f[i-1][j][x][y]\times 2^{i-1}
f[i][j][x][y]+=f[i−1][j][x][y]×2i−1
当然 y = 0 y=0 y=0的时候需要特判
观察这个转移,我们发现 x , y x,y x,y已经基本无关了,唯一有影响的就是是否是 0 0 0,所以我们可以变成 f [ i ] [ j ] [ 0 / 1 ] [ 0 / 1 ] f[i][j][0/1][0/1] f[i][j][0/1][0/1],分别表示目前是否有白点和黑点,那么这样复杂度就变成了 O ( n ) 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;
}