【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);
}
posted @ 2019-01-23 22:08  Newuser233  阅读(11)  评论(0)    收藏  举报