[FJWC2020 Day1T1] 人生

题目描述
现在你有nnn个点,每个点有黑色(000)或者白色(111)或者没有颜色(−1-11),现在你需要把所有没有颜色的点染成黑色或者白色
你还需要添加一些不重复的有向边(i,j)(i,j)(i,j),要求1≤i<j≤n1\leq i<j\leq n1i<jn,也就是说这些边需要从编号小的走到编号大的
一般的,一条合法的路径是指他经过的任意两个点的颜色不同。特别的,一个点也算作一条路径
问最后有多少种符合条件的图,使得这个图上有奇数条合法路径,答案对998244353998244353998244353取模

对于10%10\%10%的数据,n≤5n\leq 5n5
对于40%40\%40%的数据,n≤50n\leq 50n50
对于50%50\%50%的数据,n≤150n\leq 150n150
对于65%65\%65%的数据,n≤500n\leq 500n500
对于80%80\%80%的数据,n≤5000n\leq 5000n5000
对于全部的数据 ,n≤2×105n\leq2\times 10^5n2×105


solution
首先考虑对于确定的图,我们怎么计算可不可行
我们用cnticnt_icnti表示以第iii个点为结尾的路径个数,那么我们应该有这样的转移
cnti=∑j=1i−1 cntj(colori≠colorj,∃(j,i))+1cnt_i=\begin{matrix}\sum_{j=1}^{i-1}\end{matrix}\ cnt_j(color_i\neq color_j,\exist(j,i))+1cnti=j=1i1 cntj(colori=colorj,(j,i))+1
然后我们只需要判断∑cnti\sum cnt_icnti的奇偶性即可
那么我们发现我们记录出这个东西有点冗余,我们让cnticnt_icnti表示以iii点为结尾的路径个数的奇偶性,那么我们之前的转移方程可以写成
cnti=(xorsum⁡j=1i−1 cntj)xor⁡1(colori≠colorj,∃(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=1i1 cntj)xor1(colori=colorj,(j,i))
其中xorsum⁡\operatorname{xorsum}xorsum表示异或和

然后我们考虑进行dpdpdp,因为题目中说所有边都是从编号小的连向大的,所以我们用fi,j,x,yf_{i,j,x,y}fi,j,x,y表示前iii个点,cntcntcntxorsum⁡\operatorname{xorsum}xorsumjjj,有xxxcnt=1cnt=1cnt=1的白色点,有yyycnt=1cnt=1cnt=1的黑色点,的方案数,转移以将这个点染成白色为例,分两类进行讨论,一个是这个点的cnt=1cnt=1cnt=1,转移为

f[i][j][x+1][y]+=f[i−1][jxor⁡1][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^xf[i][j][x+1][y]+=f[i1][jxor1][x][y]×calc(y,0)×2x

cnti=0cnt_i=0cnti=0时,

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

其中calc⁡(y,opt)\operatorname{calc}(y,opt)calc(y,opt)表示在一个大小为yyy的集合中,奇偶性为optoptopt的子集的方案数(包括∅)

暴力枚举选了几个,这个转移就是O(n4)O(n^4)O(n4)的,可以拿到404040

我们考虑calc⁡(y,opt)\operatorname{calc}(y,opt)calc(y,opt)等于多少,我们发现在y≥1y\geq1y1他一定是2y−12^{y-1}2y1,为什么呢?
yyy为奇数的时候,我们把它看成二进制,那么任意一个奇数个数的集合一定对应一个大小为偶数的集合即他的补集,所以奇数偶数各占一半
yyy为偶数的时候,我们考虑分成1,n−11,n-11,n1两部分,已知n−1n-1n1中选出奇数和偶数的方案数是相同的,那么如果选剩下的一个那么就是n−1n-1n1中选奇数的方案,不选就是n−1n-1n1中选偶数的方案,所以奇数偶数数量还是相同的

考虑边界条件,当y=0y=0y=0时,000个数中选偶数的方案数为111000个数中选奇数的方案为000,注意特判就可以

那么现在转移就变成了O(n3)O(n^3)O(n3)的,可以拿到505050

我们发现iii这一维可以滚动数组滚掉,空间变成平方,在开O2的情况下应该可以拿到656565

考虑进一步优化,我们把calc⁡\operatorname{calc}calc的值带进之前的转移方程,变成

f[i][j][x+1][y]+=f[i−1][jxor⁡1][x][y]×2i−1(2x=2i−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[i1][jxor1][x][y]×2i1(2x=2iy)
f[i][j][x][y]+=f[i−1][j][x][y]×2i−1f[i][j][x][y]+=f[i-1][j][x][y]\times 2^{i-1}f[i][j][x][y]+=f[i1][j][x][y]×2i1

当然y=0y=0y=0的时候需要特判

观察这个转移,我们发现x,yx,yx,y已经基本无关了,唯一有影响的就是是否是000,所以我们可以变成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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值