【NOIP2017提高A組模擬10.7】Adore

題目

小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,而路徑數只分奇偶,那可以用二進制來表示,
fi,s 第i層的路徑數狀態位s的方案數。
根據邊轉移就可以了,
但是這樣時間複雜度位O(n2kk2)
考慮優化,讀入的每個點連出去的邊都可以用二進制來表示,用位運算,這樣就變成O(nk2k) 的了。

#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]);
}
發佈了219 篇原創文章 · 獲贊 229 · 訪問量 8萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章