1. 程式人生 > >hdu 5117 數學公式展開 + dp

hdu 5117 數學公式展開 + dp

return nbsp clu pri make n) for inf bsp

題目大意:有n個燈泡,m個按鈕,(1 <= n, m <= 50),每個按鈕和ki 個燈泡相關, 按下後,轉換這些燈泡的狀態,問你所有2^m的按下按鈕的

組合中亮著的燈泡的數量的三次方的和。

思路:要是將所有燈泡混在一起算很難算,我們先考慮 所有2^m的按下按鈕的 組合中亮著的燈泡的數量的和, 我們可以枚舉每個燈泡然後,計算出

有多少中情況這個燈泡是亮著的,然後全部求和。

現在是三次方的和,所以要變形一下, 我們設xi 為第i 個燈泡的狀態(1開, 0關)那麽對於每一種情況我們求的是

(x1 + x2 + x3 + ... + xn) * (x1 + x2 + x3 + ... + xn) * (x1 + x2 + x3 + ... + xn) = sigma(xi * xj * xk),然後枚舉i,j,k用dp進行計算。
#include<bits/stdc++.h>
#define
LL long long #define fi first #define se second #define mk make_pair #define pii pair<int, int> using namespace std; const int N = 50 + 7; const int M = 1e4 + 7; const int inf = 0x3f3f3f3f; const LL INF = 0x3f3f3f3f3f3f3f3f; const int mod = 1e9 +7; const double eps=1e-6; const double
pi=acos(-1); int n, m; int dp[N][8]; bool Map[N][N]; void add(int &a, LL b) { a += b; if(a >= mod) a -= mod; } int cal(int a, int b, int c) { memset(dp, 0, sizeof(dp)); dp[0][0] = 1; for(int i = 1; i <= m; i++) { int state = 0; if(Map[i][a]) state |= 1
; if(Map[i][b]) state |= 2; if(Map[i][c]) state |= 4; for(int s = 0; s < 8; s++) { add(dp[i][s], dp[i - 1][s]); add(dp[i][s], dp[i - 1][s ^ state]); } } return dp[m][7]; } int main() { int T; scanf("%d", &T); for(int cas = 1; cas <= T; cas++) { memset(Map, false, sizeof(Map)); scanf("%d%d", &n, &m); for(int i = 1; i <= m; i++) { int k; scanf("%d", &k); while(k--) { int x; scanf("%d", &x); Map[i][x] = true; } } int ans = 0; for(int i = 1; i <= n; i++) { for(int j = 1; j <= n; j++) { for(int k = 1; k <= n; k++) { add(ans, cal(i, j, k)); } } } printf("Case #%d: %d\n", cas, ans); } return 0; } /* */

hdu 5117 數學公式展開 + dp