【loj#6497. 「雅礼集训 2018 Day1」图】神奇dp
一道神奇的dp题,状态十分鬼畜。
loj 6497图
题目描述
有一张 n 个点的图,每个点可以是黑色或者白色,其中一些点已经确定了颜色。
图中一开始没有边,对于每对 i < j,你可以从 i 向 j 连一条有向边,也可以不连。
定义交错路为相邻点颜色不同的有向路径,求有多少种情况图中的交错路有奇数或偶数条。两种情况不同当且仅当有节点颜色不同或者有一条边的存在性不同。
输入格式
第一行包括两个正整数 n, p。 若 p = 0 表示要求交错路为偶数条,若 p = 1表示要求交错路为奇数条。
第二行 n 个整数,第 i 个整数若为 0,节点 i 为白,若为 1,节点 i 为黑,若为 -1,节点 i 颜色不确定。
输出格式
输出一个非负整数,表示答案对 998244353 取模后的结果。
样例
样例输入 1
3 1
-1 0 1
样例输出 1
6
数据范围与提示
对于全部数据,$ 1 \leq n \leq 2×10^5 $
5
啥,奇偶?肯定要奇偶分开成dp的0/1状态,可是然后呢?
好吧,我们设定dp状态$f[i][kind][x][y]$
表示讨论到第i个点,kind目前的路径条数的为奇数还是偶数,x表示是否存在以黑点结尾并且以这个点结尾的路径条数为奇数,y表示是否存在以白点结尾并且以这个点结尾的路径条数为奇数。
x,y的作用?
比如我们i+1选白点,并且x为0,我们发现不管怎么往前面连边,路径增加的条数总为奇数条(如果连向白点,没有新的路径贡献,如果连向黑点,原本以他们结尾全部为偶,多连之后只会产生成奇数条)。这样就可以转移到$f[i][kind][x][y]* (2^{i}) \ \rightarrow \ f[i+1][kind\oplus 1][x][1]$
否则x为1的话,就有可能要讨论新增的路径条数为奇或偶。根据考虑,发现这取决于选了连奇数结尾的个数,再一考虑,发现居然转移到奇和偶的方案系数一样都是一样的为$2^{i-1} $
那么有
$f[i][kind][x][y] * (2^{i-1}) \ \rightarrow \ f[i+1][kind][x][y]$ 和
$f[i][kind][x][y] * (2^{i-1}) \ \rightarrow \ f[i+1][kind\oplus 1][x][1]$
同理选黑点又有转移
如果y为0$f[i][kind][x][y]* (2^{i}) \ \rightarrow \ f[i+1][kind\oplus 1][1][y] $
否则
$f[i][kind][x][y] * (2^{i-1}) \ \rightarrow \ f[i+1][kind][x][y]$ 和
$f[i][kind][x][y] * (2^{i-1}) \ \rightarrow \ f[i+1][kind\oplus 1][1][y]$
code:
#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<cstdio>
using namespace std;
const int mod = 998244353;
int add(int x,int y) { x+=y; return x>=mod?x-mod:x; }
int sub(int x,int y) { x-=y; return x<0?x+mod:x; }
int mul(int x,int y) { return 1ll*x*y%mod; }
void upd(int &x,int y) { x+=y; if(x>=mod)x-=mod; }
int ksm(int a,int b) {
int ans = 1;
for(;b;b>>=1,a=mul(a,a))
if(b&1) ans = mul(ans,a);
return ans;
}
int n,p;
int f[200005][2][2][2];
int pm[200005];
int main() {
scanf("%d%d",&n,&p);
f[0][0][0][0] = 1;
pm[0] = 1; for(int i=1;i<=n;i++) pm[i] = mul(pm[i-1],2);
for(int i=0;i<n;i++) {
int o; scanf("%d",&o);
for(int kd=0;kd<2;kd++) {
for(int x=0;x<2;x++) {
for(int y=0;y<2;y++) {
int T = f[i][kd][x][y];
if(!T) continue;
if(o!=1) { // chose to white
if(x==0) {
upd(f[i+1][kd^1][x][1],mul(pm[i],T));
} else{
if(i)upd(f[i+1][kd][x][y],mul(pm[i-1],T));
if(i)upd(f[i+1][kd^1][x][1],mul(pm[i-1],T));
}
}
if(o!=0) { // chose to black
if(y==0) {
upd(f[i+1][kd^1][1][y],mul(pm[i],T));
} else {
if(i)upd(f[i+1][kd][x][y],mul(pm[i-1],T));
if(i)upd(f[i+1][kd^1][1][y],mul(pm[i-1],T));
}
}
}
}
}
}
int ans = 0;
for(int x=0;x<2;x++) {
for(int y=0;y<2;y++) upd(ans,f[n][p][x][y]);
}
printf("%d",ans);
}