题目链接:点击这里
题意:n个灯,m个开关,一共有 2m 种开关的状态,每一个开关可以将某些灯泡的状态异或。求出每一种开关状态下 X3 的和,X表示开关状态下亮的灯泡
假设一种开关状态下的灯泡状态是 x1,x2,…xn ,那么对答案的贡献就是 (x1+x2+⋯+xn)3=∑ni=1∑nj=1∑nk=1(xi∗xj∗xk) ,当且仅当这三个都是1才有效。经过这么观察就可以发现,最后的答案等于使得 (xi,xj,xk) 的乘积等于1的方案数。于是就暴力枚举三个下标,然后用正常的DP来转移即可。
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <iostream>
#include <vector>
#include <cstring>
using namespace std;
#define maxn 55
#define mod 1000000007
int dp[maxn][2][2][2];
int n, m;
bool a[maxn][maxn];
void solve () {
memset (dp, 0, sizeof dp);
int ans = 0;
for (int i = 1; i <= n; i++) for (int j = 1; j <= n; j++) for (int k = 1; k <= n; k++) {
memset (dp, 0, sizeof dp);
dp[0][0][0][0] = 1;
for (int now = 1; now <= m; now++) {
for (int ii = 0; ii < 2; ii++) for (int jj = 0; jj < 2; jj++) for (int kk = 0; kk < 2; kk++) {
(dp[now][ii^a[now][i]][jj^a[now][j]][kk^a[now][k]] += dp[now-1][ii][jj][kk]) %= mod;
(dp[now][ii][jj][kk] += dp[now-1][ii][jj][kk]) %= mod;
}
}
(ans += dp[m][1][1][1]) %= mod;;
}
printf ("%d\n", ans);
}
int main () {
int t, kase = 0;
scanf ("%d", &t);
while (t--) {
scanf ("%d%d", &n, &m);
memset (a, 0, sizeof a);
for (int i = 1; i <= m; i++) {
int num; scanf ("%d", &num);
for (int j = 0; j < num; j++) {
int id; scanf ("%d", &id);
a[i][id] = 1;
}
}
printf ("Case #%d: ", ++kase);
solve ();
}
return 0;
}