题目描述
小w偶然间见到了一个DAG。这个DAG有m层,第一层只有一个源点,最后一层只有一个汇点,剩下的每一层都有k个节点。现在小w每次可以取反第i(1<i<n-1)层和第i+1层之间的连边。也就是把原本从(i,k1)连到(i+1,k2)的边,变成从(i,k2)连到(i+1,k1)。请问他有多少种取反的方案,把从源点到汇点的路径数变成偶数条?答案对998244353取模。
输入描述
一行两个整数m,k。接下来m-1行,第一行和最后一行有k个整数0或1,剩下每行有k2个整数0或1,第(j-1)×k+t个整数表示(i,j)到(i+ 1,t)有没有边。
输出描述
一行一个整数表示答案。
输入样例
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
输出样例
4
20%的数据满足n≤10,k≤2。
40%的数据满足n≤103,k≤2。
60%的数据满足m≤103,k≤5。
100%的数据满足4≤m≤104,k≤10。
分析
看不懂题目手玩样例玩了一个小时都没玩出来最后发现自己少连了一条边
由于路径数量并不需要知道具体的值,只需要知道奇偶性就行,而且发现k还这么小,所以考虑状压dp
dp[i][s]表示到达i层时 到达各点方案数的奇偶性情况为s 的方案数。
这个可以根据题目中给出的边求出下一个状态,因为存在反转所以每次有两种转移方式
于是某个憨憨高兴地用k2的转移方法获得了60pts的好成绩
对于每次给的边,我们可以预处理出每个点与下一层点的连边情况并状压起来
因为只有奇数转移才会影响状态,所以只需要找到方案数为奇数的点然后让下一个状态异或上之前预处理好的它的连边状态就可以得到下一层的状态了
Code
#include<cstdio>
const int mod=998244353;
int n,K,st,ans,dp[20005][1025],V[15][15];
int main()
{
scanf("%d%d",&n,&K);
for(int i=1,a;i<=K;i++)scanf("%d",&a),st=(st<<1)+a;dp[1][st]=1;
for(int i=1;i<n-2;i++)
{
int a[12]={0},b[12]={0};
for(int j=K;j>=1;j--)for(int k=K,o;k>=1;k--)
scanf("%d",&o),a[j]|=o<<(k-1),b[k]|=o<<(j-1);
for(st=0;st<=(1<<K)-1;st++)if(dp[i][st])
{
int ed1=0,ed2=0;
for(int j=1;j<=K;j++)if(st&(1<<(j-1)))ed1^=a[j],ed2^=b[j];
(dp[i+1][ed1]+=dp[i][st])%=mod;
(dp[i+1][ed2]+=dp[i][st])%=mod;
}
}
for(int i=K;i>=1;i--)scanf("%d",&V[i][1]);
for(st=0;st<=(1<<K)-1;st++)if(dp[n-2][st])
{
int cnt=0;
for(int j=1;j<=K;j++)if(st&(1<<(j-1))&&V[j][1])cnt^=1;
if(cnt==0)(ans+=dp[n-2][st])%=mod;
}
printf("%d
",ans);
}