bzoj3195: [Jxoi2012]奇怪的道路(狀壓dp)
Description
小宇從歷史書上了解到一個古老的文明。這個文明在各個方面高度發達,交通方面也不例外。考古學家已經知道,這個文明在全盛時期有n座城市,編號為1..n。m條道路連接在這些城市之間,每條道路將兩個城市連接起來,使得兩地的居民可以方便地來往。一對城市之間可能存在多條道路。
據史料記載,這個文明的交通網絡滿足兩個奇怪的特征。首先,這個文明崇拜數字K,所以對於任何一條道路,設它連接的兩個城市分別為u和v,則必定滿足1 <=|u - v| <= K。此外,任何一個城市都與恰好偶數條道路相連(0也被認為是偶數)。不過,由於時間過於久遠,具體的交通網絡我們已經無法得知了。小宇很好奇這n個城市之間究竟有多少種可能的連接方法,於是她向你求助。
Input
輸入共一行,為3個整數n,m,K。
Output
輸出1個整數,表示方案數模1000000007後的結果。
Sample Input
【輸入樣例1】3 4 1
【輸入樣例2】
4 3 3
Sample Output
【輸出樣例1】3
【輸出樣例2】
4
【數據規模】
HINT
100%的數據滿足1
<= n <= 30, 0 <= m <= 30, 1 <= K <= 8.
【題目說明】
兩種可能的連接方法不同當且僅當存在一對城市,它們間的道路數在兩種方法中不同。
在交通網絡中,有可能存在兩個城市無法互相到達。
我討厭數數題……LadyLex大佬太強啦
因為$k$很小,我們考慮狀壓,而且因為奇偶是一種相反的概念,可以用$01$表示
我們可以用二進制數表示$i$可以轉移到的所有數的奇偶性,但是那樣的話有$2^{16}$種可能,太大了
然後發現我們其實只要考慮一邊的狀態就好了,另一邊的狀態可以在枚舉到後面的時候再考慮
於是我們只要考慮第$i$個點以及前面的$k$個點就好了,然後再加一維表示我們考慮到了前面的第$l$個點
那麽狀態就設為$dp[i][j][u][l]$,表示考慮到第$i$個點,已經連了$j$條邊,前$k$個點連邊的奇偶性為$u$,且前$k$個點已經考慮到第$l$個點
然後我們考慮轉移對於第$i-k+l$個點和第$i-k+l+1$個點,如果我們不再加邊,可以直接轉移,即$dp[i][j][u][l]$可以轉移到$dp[i][j][u][l+1]$
然後如果在還合法的情況下加邊,會同時改變兩個點的奇偶性,也就會轉移到$dp[i][j][u\ xor\ 2^k\ xor\ 2^l][l]$(狀態裏第$0$位代表左邊第$k$個點,第$k$位表示點$i$)
然後考慮不同$i$之間的轉移,如果要轉移的話再也不會考慮到左邊第$k$個點了,那麽左邊第$k$個點的奇偶性必須是偶數才行,即$u\&1==0$
然後對於一個點$i$,它的下一個點的狀態就是$l>>1$,所以直接轉移
然後最後的答案就是$dp[n+1][m][0][0]$
1 //minamoto 2 #include<bits/stdc++.h> 3 #define ll long long 4 using namespace std; 5 const int mod=1e9+7; 6 int n,m,k,bin[20],dp[35][35][(1<<9)+5][11]; 7 int main(){ 8 scanf("%d%d%d",&n,&m,&k); 9 bin[0]=1;for(int i=1;i<=15;++i) bin[i]=bin[i-1]<<1; 10 dp[2][0][0][0]=1; 11 for(int i=2;i<=n;++i) for(int j=0;j<=m;++j) for(int u=0;u<bin[k+1];++u){ 12 for(int l=0;l<k;++l) 13 if(dp[i][j][u][l]){ 14 (dp[i][j][u][l+1]+=dp[i][j][u][l])%=mod; 15 if(j<m&&i-k+l>0) 16 (dp[i][j+1][u^bin[k]^bin[l]][l]+=dp[i][j][u][l])%=mod; 17 } 18 if((u&1)==0&&dp[i][j][u][k]) 19 dp[i+1][j][u>>1][0]=dp[i][j][u][k]; 20 } 21 printf("%d\n",dp[n+1][m][0][0]); 22 return 0; 23 }
bzoj3195: [Jxoi2012]奇怪的道路(狀壓dp)