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);
}
以仁心说,以学心听,以公心辩