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

本文探讨了一个关于有向无环图(DAG)的问题,即通过反转某些边的方向来改变从源点到汇点的路径数量,并求解使得路径数量为偶数的不同方案总数。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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 998244353
using 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);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值