题目描述
小$w$偶然间见到了一个$DAG$。
这个$DAG$有$m$层,第一层只有一个源点,最后一层只有一个汇点,剩下的每一层都有$k$个节点。
现在小$w$每次可以取反第$i(1<i<n-1)$层和第$i+1$层之间的连边。也就是把原本从$(i,k_1)$连到$(i+1,k_2)$的边,变成从$(i,k_2)$连到$(i+1,k_1)$。
请问他有多少种取反的方案,把从源点到汇点的路径数变成偶数条?
答案对$998244353$取模。
输入格式
一行两个整数$m,k$。
接下来$m-1$行,第一行和最后一行有$k$个整数$0$或$1$,剩下每行有$k^2$个整数$0$或$1$,第$(j-1)\times 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\leqslant 10,k\leqslant 2$。
$40\%$的数据满足$n\leqslant 10^3,k\leqslant 2$。
$60\%$的数据满足$m\leqslant 10^3,k\leqslant 5$。
$100\%$的数据满足$4\leqslant m\leqslant 10^4,k\leqslant 10$。
题解
发现$k$很小,考虑状压$DP$,设$dp[i][s]$表示第$i$行,能连边的点的状态为$s$的方案数。
转移用记忆化搜索即可,从后往前搜索。
时间复杂度:$\Theta(NK2^K)$。
期望得分:$100$分。
实际得分:$100$分。
代码时刻
#include<bits/stdc++.h> using namespace std; const int mod=998244353; int M,K,S; int Map[2][10001][11],a[11],g[1025]; long long dp[10001][1025]; int lowbit(int x){return x&-x;} long long dfs(int x,int s) { if(dp[x][s]!=-1)return dp[x][s]; if(x==2) { dp[x][s]=1; for(int i=1;i<=K;i++)dp[x][s]^=a[i]&((s&(1<<(i-1)))!=0); } else { int ls=0,rs=0; for(int i=1;i<=K;i++) { ls|=g[Map[0][x-1][i]&s]<<(i-1); rs|=g[Map[1][x-1][i]&s]<<(i-1); } dp[x][s]=(dfs(x-1,ls)+dfs(x-1,rs))%mod; } return dp[x][s]; } int main() { memset(dp,-1,sizeof(dp)); scanf("%d%d",&M,&K); for(int i=1;i<(1<<K);i++)g[i]=g[i-lowbit(i)]^1; for(int i=1;i<=K;i++)scanf("%d",&a[i]); for(int i=2;i<M-1;i++) for(int j=1;j<=K;j++) for(int k=1;k<=K;k++) { int x;scanf("%d",&x); Map[0][i][j]|=x<<(k-1); Map[1][i][k]|=x<<(j-1); } for(int i=1;i<=K;i++) { int x; scanf("%d",&x); S|=x<<(i-1); } printf("%lld",dfs(M-1,S)); return 0; }
rp++
原文地址:https://www.cnblogs.com/wzc521/p/11832842.html