JZOJ5398. 【NOIP2017提高A组模拟10.7】Adore

来源:互联网 发布:php 连缀 方法 编辑:程序博客网 时间:2024/06/14 06:50

Description

小w 偶然间见到了一个DAG。
这个DAG 有m 层,第一层只有一个源点,最后一层只有一个汇点,剩下的每一层都有k 个节点。
现在小w 每次可以取反第i(1 < i < n - 1) 层和第i + 1 层之间的连边。也就是把原本从(i, k1) 连到(i + 1, k2) 的边,变成从(i, k2) 连到(i + 1, k1)。
请问他有多少种取反的方案,把从源点到汇点的路径数变成偶数条?
答案对998244353 取模。

Input

一行两个整数m, k。
接下来m - 1 行, 第一行和最后一行有k 个整数0 或1,剩下每行有k2 个整数0 或1,第(j- 1)* k + t 个整数表示(i, j) 到(i + 1, t)有没有边。

Output

一行一个整数表示答案。

Sample Input

5 3
1 0 1
0 1 0 1 1 0 0 0 1
0 1 1 1 0 0 0 1 1
0 1 1

Sample Output

4

Data Constraint

20% 的数据满足n <= 10, k <= 2
40% 的数据满足n <= 10^3, k <= 2。
60% 的数据满足m <= 10^3, k <= 5。
100% 的数据满足4 <= m <= 10^4, k <= 10。

题解

最终的答案只是与奇偶性有个,
那么就只保留它的奇偶性。

而且这里的k很小,就可以状态压缩。
fi,s 表示在第i行,状态为s的方案数。

转移的时候是整行转移。

code

#include<queue>#include<cstdio>#include<iostream>#include<algorithm>#include <cstring>#include <string.h>#include <cmath>#include <math.h>#define ll long long#define N 10003#define db double#define P putchar#define G getchar#define mo 998244353using namespace std;char ch;void read(int &n){    n=0;    ch=G();    while((ch<'0' || ch>'9') && ch!='-')ch=G();    ll w=1;    if(ch=='-')w=-1,ch=G();    while('0'<=ch && ch<='9')n=(n<<3)+(n<<1)+ch-'0',ch=G();    n*=w;}ll max(ll a,ll b){return a>b?a:b;}ll min(ll a,ll b){return a<b?a:b;}ll abs(ll x){return x<0?-x:x;}void write(ll x){if(x>9) write(x/10);P(x%10+'0');}int f[N][2048],n,m,k,t,z[13],g[13],w[13],ans,num[2048];int main(){    freopen("adore.in","r",stdin);    freopen("adore.out","w",stdout);    z[0]=1;for(int i=1;i<13;i++)z[i]=z[i-1]*2;    read(n);read(k);m=0;    for(int i=0;i<k;i++)        read(t),m|=z[i]*t;    f[1][m]=1;    for(int s=0;s<=z[k];s++)        num[s]=num[s>>1]^(s&1);                 for(int i=1;i<n-2;i++)    {        memset(w,0,sizeof(w));        memset(g,0,sizeof(g));        for(int j=0;j<k;j++)            for(int p=0;p<k;p++)            {                read(t);                g[j]|=(t<<p);                w[p]|=(t<<j);            }        for(int s=0;s<z[k];s++)             if(f[i][s])            {                int s1=0,s2=0;                for(int j=0;j<k;j++)                    s1|=(num[s&w[j]]<<j),s2|=(num[s&g[j]]<<j);                f[i+1][s1]=(f[i+1][s1]+f[i][s])%mo;                f[i+1][s2]=(f[i+1][s2]+f[i][s])%mo;            }    }    m=0;        for(int i=0;i<k;i++)        read(t),m|=t<<i;    ans=0;    for(int s=0;s<z[k];s++)        ans=(ans+(num[s&m]?0:f[n-2][s]))%mo;    write(ans);}
原创粉丝点击