题目:http://acm.hdu.edu.cn/showproblem.php?pid=3364
题意:n盏灯,m个开关控制,给出每个开关控制的灯序号,求要使所以等为某个状态的方法数(每个开关只有2种状态)。
思路:高斯消元求解方程组的解,m个开关的状态为变量,n盏灯的状态为n条方程的解,异或方程。
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <math.h>
#include <stdlib.h>
#define INF 0x7fffffff
#define MOD 1000000007
using namespace std;
typedef long long ll;
int n, m;
int init[55][55], a[55][55], x[55];
//int free_x[55], free_num;
int Gauss()
{
int r, c, k;
//free_num = 0;
for(k = 0, c = 0; k < n && c < m; k++, c++)
{
r = k;
for(int i = k + 1; i < n; i++)
{//找正在处理的第c个变量系数最大的方程
if(abs(a[i][c]) > abs(a[r][c]))
r = i;
}
if(a[r][c] == 0)
{//这个是自由元,free_x[free_num] = c;
k--;
//free_num++;
continue;
}
if(r != k)
{//把前面找到那个方程换到第r行
for(int j = c; j < m + 1; j++)
swap(a[k][j], a[r][j]);
}
for(int i = k + 1; i < n; i++)
{
if(a[i][c] != 0)
{
for(int j = c; j < m + 1; j++)
{//异或消元,相加摸二等价于异或操作
a[i][j] ^= a[k][j];
}
}
}
}
for(int i = k; i < n; i++)
if(a[i][c]) return -1;
if(k < m) return m - k;
/*for(int i = m - 1; i >= 0; i--)
{
x[i] = a[i][m];
for(int j = i + 1; j < m; j++)
x[i] ^= (a[i][j] && x[j]);
}*/
return 0;
}
int main()
{
#ifdef LOCAL
freopen("data.in", "r", stdin);
#endif
int t, swi, k, q;
scanf("%d", &t);
for(int cas = 1; cas <= t; cas++)
{
scanf("%d%d", &n, &m);
memset(init, 0, sizeof(init));
for(int i = 0; i < m; i++)
{
scanf("%d", &k);
for(int j = 0; j < k; j++)
{
scanf("%d", &swi);
init[swi - 1][i] = 1;
}
}
scanf("%d", &q);
printf("Case %d:\n", cas);
for(int i = 0; i < q; i++)
{
for(int j = 0; j < n; j++)
{
scanf("%d", &a[j][m]);
}
for(int i = 0; i < n; i++)
for(int j = 0; j < m; j++)
a[i][j] = init[i][j];
int ans = Gauss();
if(ans == -1)
printf("0\n");
else printf("%I64d\n", (ll)1 << ans);
}
}
return 0;
}