1. 程式人生 > >bzoj 3195: [Jxoi2012]奇怪的道路

bzoj 3195: [Jxoi2012]奇怪的道路

連結:3195: [Jxoi2012]奇怪的道路

  • 大意:給定\(n\)\(m\)\(k\),求出滿足下列兩個要求的圖的個數,允許重邊己環和不聯通:
  • 每條邊\(1<=|u-v|<=K\)且每個點連邊偶數。$n,m\leq 30 \(,\)k\leq 8$
  • 有個樸素的\(Dp\)就是狀壓\(k\),設\(f_{i,j,s}\)表示前\(i\)個點,\(j\)條邊,最後\(k\)個點的奇偶性為\(s\)的方案數。
  • 然後列舉\(i\)和誰連邊向\(j+1\)轉移,每個\(s\)再向\(i+1\)轉移。
  • 寫完後發現這個做法有\(bug\),因為同一個狀態因為\(i\)
    向外連邊的順序不同而被重複記數了。
  • 其實很好解決,\(i\)連向相同一個點的邊就很像揹包問題中的一件物品,每一件物品都是不限量的(只是最終要求了奇偶)。
  • 想一想,我們的完全揹包統計方案時並沒有多開一維,但絕對不會重複計數的。
  • 原因很簡單,在做揹包問題時,各個物品的選取存在嚴格順序,不存在選了幾個物品\(a\),又選了一點別的,再去選了幾個物品\(a\)的情況。
  • 所以這裡應當嚴格區分出各個物品,在每一個物品內部做完全揹包。
  • 程式碼很短
#include<bits/stdc++.h>
#define R register int
using namespace std;
const int mod=1000000007;
int n,m,k,now,lm,f[2][35][600];
void add(R &x,R y){x=(x+y>=mod?x+y-mod:x+y);}
int main(){
    freopen("s.in","r",stdin);
    cin>>n>>m>>k,lm=(1<<(k+1)),f[now][0][0]=1;
    for(R i=1;i<=n;++i){
        now^=1,memset(f[now],0,sizeof(f[now]));
        for(R j=0;j<=m;++j)
            for(R S=0;S<(1<<k);++S)
                f[now][j][S<<1]=f[now^1][j][S];
        for(R p=1;p<=min(i-1,k);++p)
            for(R j=0;j<=m;++j)
                for(R S=0;S<lm;++S)
                    add(f[now][j+1][S^1^(1<<p)],f[now][j][S]);
    }
    cout<<f[now][m][0];
    return 0;
}