題目
小w 偶然間見到了一個DAG。
這個DAG 有m 層,第一層只有一個源點,最後一層只有一個匯點,剩下的每一層都有k 個節點。
現在小w 每次可以取反第i(1 < i < n - 1) 層和第i + 1 層之間的連邊。也就是把原本從(i, k1) 連到(i + 1, k2) 的邊,變成從(i, k2) 連到(i + 1, k1)。
請問他有多少種取反的方案,把從源點到匯點的路徑數變成偶數條?
答案對998244353 取模。
狀壓dp
考慮用dp,
因爲k<=10,而路徑數只分奇偶,那可以用二進制來表示,
設
根據邊轉移就可以了,
但是這樣時間複雜度位
考慮優化,讀入的每個點連出去的邊都可以用二進制來表示,用位運算,這樣就變成
#include <cmath>
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <queue>
#include <map>
const int maxlongint=2147483647;
const int mo=998244353;
const int N=10005;
using namespace std;
int f[N][1034];
int n,m,b[N][12][12],mi[12],fb[N][12][12],bb[N][12],bc[N][12];
int read(int &n)
{
char ch=' ';int q=0,w=1;
for(;(ch!='-')&&((ch<'0')||(ch>'9'));ch=getchar());
if(ch=='-')w=-1,ch=getchar();
for(;ch>='0' && ch<='9';ch=getchar())q=(q<<1)+(q<<3)+ch-'0';n=q*w;return n;
}
int main()
{
mi[0]=1;
for(int i=1;i<=11;i++) mi[i]=mi[i-1]*2;
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++) read(b[1][1][i]),bb[1][1]+=b[1][1][i]*mi[i-1];
for(int i=2;i<=n-2;i++)
for(int j=1;j<=m;j++)
for(int k=1;k<=m;k++) read(b[i][j][k]),fb[i][k][j]=b[i][j][k],bb[i][j]+=b[i][j][k]*mi[k-1],bc[i][k]+=fb[i][k][j]*mi[j-1];
for(int i=1;i<=m;i++) read(b[n-1][i][1]),bb[n-1][i]+=b[n-1][i][1];
f[1][1]=1;
for(int i=1;i<=n-1;i++)
for(int j=0;j<=mi[m]-1;j++)
{
int t=f[i][j];
if(t)
{
int tt=0;
for(int k=1;k<=m;k++)
tt^=bb[i][k]*bool(mi[k-1]&j);
f[i+1][tt]=(1ll*f[i+1][tt]+t)%mo;
if(i==1 || i==n-1) continue;
tt=0;
for(int k=1;k<=m;k++)
tt^=bc[i][k]*bool(mi[k-1]&j);
f[i+1][tt]=(1ll*f[i+1][tt]+t)%mo;
}
}
printf("%lld",f[n][0]);
}