这个题简单DP可以通过,然而前面需要数学推导一下。
题意简单说一下:有n个灯泡,和m个开关,每个开关控制着一些灯的明亮,摁下一个开关,对应这个开关的那些灯泡就会翻转。每个灯有摁下和不摁下供人选择,总共2^m种选择。求所有在2^m种选择下,所有明着的灯三次方总和。
定义Xi表示第i盏灯的状态, 亮就是1 ,不亮就是0
所以求得解就是 (x1 +x2 +x3 +x4+x5+…………+xn)*(x1 +x2 +x3 +x4+x5+…………+xn)*(x1 +x2 +x3 +x4+x5+…………+xn)
当然这三个集合是一样的,把他们展开就是好多个 xi * xj * xk
这样就可以枚举所有情况了,当然是DP优化一下
dp[a][b][c][i][j]
表示 a, b, c这三个灯泡,在前i个开关,状态为j的情况下 (0<=j<8总共8种情况对应3盏灯)
这样用循环a,b,c前三维度的数组就可以省去了
DP[i][j]表示前i个开关,这三个灯,状态为j的个数
附上AC代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn = 55;
const int maxm = 55;
const int mod = 1e9 + 7;
int K[maxm][maxn];
int T, n, m;
int kase = 0;
int tain(int x, int y, int a, int b, int c){
if(K[x][a]==1) y^=1;
if(K[x][b]==1) y^=2;
if(K[x][c]==1) y^=4;
return y;
}
int dp[maxn][8];
int main(){
scanf("%d", &T);
while(T--){
scanf("%d%d", &n, &m);
memset(K, 0, sizeof(K));
for(int i = 1; i<=m;++i){
int x, y;
scanf("%d", &x);
while(x--){
scanf("%d", &y);
K[i][y] = 1;
}
}
int ans = 0;
for(int a = 1; a <= n; ++a){
for(int b = 1; b <= n; ++b)
for(int c = 1; c <= n; ++c){
for(int i = 0; i<8; ++i)
dp[0][i] = (i==0);
for(int i = 1; i<=m; ++i){
for(int j = 0; j<8; ++j){
dp[i][j] = dp[i-1][j] + dp[i-1][tain(i, j, a, b, c)];
if(dp[i][j]>=mod) dp[i][j]%=mod;
}
}
ans = (ans +dp[m][7])%mod;
}
}
printf("Case #%d: %d\n", ++kase, ans);
}
return 0;
}