LOJ 6497 图

LOJ 6497 图

题意

有图\(n\)点,每点可为黑或白,其中一些点颜色已定。

初时图无边,于每对\(i<j\),可由\(i\)\(j\)连有向边,或不连。

称黑白相间之路径为交错路径。

求:有多少种情况交错路径有奇数条或偶数条。

数据范围:$$n\le2*10^5$$

注:单点也算一条交错路径。

思路

在后方新增一点\(k+1\),交错路径的数量怎么改变。

显然,新增的交错路径必须以新增的点结尾。

\(sum_x\)表示以\(x\)结尾的交错路径的数量,\(S\)表示向\(k+1\)连边的且与\(k+1\)不同色的点的集合。

那么,新增交错路径为:

\[(\sum_{x\in S}{sum_x})+1 \]

这个应该比较好理解。

显然,我们只关注这个式子的奇偶性。

那么,关键在于奇点了。

对于同色点,我们可以随意连边。

对于异色且\(sum\)值为偶数的点,我们也可以随意连边。

然后,我们就可以\(O(n^3)\)DP了,记录当前有多少个黑色的奇数点,多少个白色的奇数点。

我们尝试优化一下。

可以发现,目前对奇数点的处理并不优。

我们发现,一个奇数点就足以改变整个状态。

于是,我们任意钦定一个异色奇数点,先不动。

其他奇数点随意连边。

观察我们现在的状态和期望转移到的状态有无差异,以此决定是否把钦定的这个点连上。

此时,我们有\(2^{k-1}\)种连法(除了钦定的点,其他随便连)

那么,如果没有一个异色奇数点呢?

无论如何连边,你都将改变交错路径的奇偶性。

此时,我们有\(2^{k}\)种连法。

设计状态

\[dp[i][0/1][0/1][0/1] \]

表示到第\(i\)个点,此时交错路径总数的奇偶性,有无白色奇数点,有无黑色奇数点

随便转移即可。

代码

#include<bits/stdc++.h>
using namespace std;
const int mod=998244353;
const int sz=2e5+7;
int n,p;
int a[sz];
int qp[sz];
int f[sz][2][2][2];
void upd(int &x,int y){
	x=x+y>=mod?x+y-mod:x+y;
}
void init(){
	qp[0]=1;
	for(int i=1;i<sz;i++) qp[i]=2*qp[i-1]%mod;
}
int main(){
	init();
	scanf("%d%d",&n,&p);
	for(int i=1;i<=n;i++) scanf("%d",&a[i]);
	f[0][0][0][0]=1;
	for(int i=0;i<n;i++)
		for(int k=0;k<=1;k++)
			for(int x=0;x<=1;x++)
				for(int y=0;y<=1;y++){
					int cur=f[i][k][x][y];
					if(!cur) continue;
					if(a[i+1]==0||a[i+1]==-1){
						if(x==0) upd(f[i+1][k^1][x][1],1ll*cur*qp[i]%mod);
						else{
							upd(f[i+1][k][x][y],1ll*cur*qp[i-1]%mod);
							upd(f[i+1][k^1][x][1],1ll*cur*qp[i-1]%mod);
						}
					}
					if(a[i+1]==1||a[i+1]==-1){
						if(y==0) upd(f[i+1][k^1][1][y],1ll*cur*qp[i]%mod);
						else{
							upd(f[i+1][k][x][y],1ll*cur*qp[i-1]%mod);
							upd(f[i+1][k^1][1][y],1ll*cur*qp[i-1]%mod);
						}
					}
				}
	int ans=0;
	upd(ans,f[n][p][0][0]);
	upd(ans,f[n][p][0][1]);
	upd(ans,f[n][p][1][0]);
	upd(ans,f[n][p][1][1]);
	printf("%d\n",ans);
}
posted @ 2019-11-14 21:52  霞光  阅读(147)  评论(0编辑  收藏  举报