【組合數學dp】ACM-ICPC 2018 徐州賽區網路預賽 A. Hard to prepare
阿新 • • 發佈:2018-12-24
Step1 Problem:
給你 n 個數排成一圈,每個數的範圍[0, 2^k-1],相鄰兩個數字它們異或值不能為 2^(k-1),求滿足條件的排列數。
資料範圍:
T<=20, 0 < n, k<=1e6.
Step2 Ideas:
我們假設 a 不能與他相鄰的數為 a1,也就是 a 異或 a1 = 2^(k-1).
第一反應:如果不是圈,那麼排列數 = 4*3^(n-1).我們把第一個是 a 最後一個是 a1 的方案數也算進去了。
第一個思考點:開始思考最後一個不能是 a1 的方案數怎麼求。
如果我們知道第 n-1 位和 第 1 位一樣的方案數 s1,第 n-1 位和 第 1 位不一樣的方案數 s2。
那麼排列數 = s1*(k-1) + s2*(k-2).
第二個思考點:就開始思考第 n-1 位和 第 1 位一樣的方案數怎麼求。
然後你會發現你需要知道第 1 位和第 n-2 位 異或值為 2^(k-1) 的方案數。
所以:
dp[n][0]: 代表第 1 位和第 n 位一樣的方案數
dp[n][1]: 代表第 1 位和第 n 位異或值位 2^(k-1) 的方案數
dp[n][2]: 剩下情況的方案數
Step3 Code:
#include<bits/stdc++.h> using namespace std; #define ll long long const int N = 1e6+5; const int MOD = 1e9+7; ll dp[N][3]; ll Pow(ll x, ll n) { ll ans = 1; while(n) { if(n&1) ans *= x, ans %= MOD; x = x*x, x %= MOD; n >>= 1; } return ans; } int main() { int T, n, k; scanf("%d", &T); while(T--) { scanf("%d %d", &n, &k); k = Pow(2, k); ll t = k; dp[1][0] = k; dp[1][1] = dp[1][2] = 0; for(int i = 2; i <= n; i++) { t = t*(k-1)%MOD; dp[i][0] = dp[i-1][0] + dp[i-1][2], dp[i][0] %= MOD; dp[i][1] = dp[i-1][1] + dp[i-1][2], dp[i][1] %= MOD; dp[i][2] = ((t-dp[i][0]-dp[i][1])%MOD+MOD)%MOD; } printf("%lld\n", (dp[n][0]+dp[n][2])%MOD); } return 0; }