HDU3364 Lanterns

本文介绍了一道算法题“Lanterns”,通过异或高斯消元方法解决灯状态变化的问题。该方法利用矩阵运算原理,以开关控制灯的变化为背景,实现对灯的目标状态配置的计数。

原题链接:http://acm.hdu.edu.cn/showproblem.php?pid=3364

Lanterns

Problem Description

Alice has received a beautiful present from Bob. The present contains n lanterns and m switches. Each switch controls some lanterns and pushing the switch will change the state of all lanterns it controls from off to on or from on to off. A lantern may be controlled by many switches. At the beginning, all the lanterns are off.

Alice wants to change the state of the lanterns to some specific configurations and she knows that pushing a switch more than once is pointless. Help Alice to find out the number of ways she can achieve the goal. Two ways are different if and only if the sets (including the empty set) of the switches been pushed are different.

Input

The first line contains an integer T (T<=5) indicating the number of test cases.
The first line of each test case contains an integer n (1<=n<=50) and m (1<=m<=50).
Then m lines follow. Each line contains an integer k (k<=n) indicating the number of lanterns this switch controls.
Then k integers follow between 1 and n inclusive indicating the lantern controlled by this switch.
The next line contains an integer Q (1<=Q<=1000) represent the number of queries of this test case.
Q lines follows. Each line contains n integers and the i-th integer indicating that the state (1 for on and 0 for off) of the i-th lantern of this query.

Output

For each test case, print the case number in the first line. Then output one line containing the answer for each query.
Please follow the format of the sample output.

Sample Input

2
3 2
2 1 2
2 1 3
2
0 1 1
1 1 1
3 3
0
0
0
2
0 0 0
1 0 0

Sample Output

Case 1:
1
0
Case 2:
8
0

题目大意

n n 盏灯,m个开关,每个开关可以控制多盏灯,每盏灯可以被多盏开关控制。开
始每盏灯都是开闭状态,给定每盏灯的最终状态,问有多少种方案可以到达。

题解

实际上就是个异或版的高斯消元,异或高斯消元和普通高斯消元没有太大差异,只是在消元时将运算从加减乘除替换为异或而已,我们以开关是否按下为未知数,以灯的状态列方程,以样例里第一组数据为例,矩阵如下:


110101011 1 1 0 1 0 1 0 1 1

其中,第一盏灯受1、2号开关控制,而最终状态要求为关闭,所以方程为 x1 xor x2=0 x 1   x o r   x 2 = 0 x1,x2 x 1 , x 2 的取值为1或0,表示开关按或不按。

消元的时候如果对应未知数的系数是1,那么就用异或来消元,0就跳过,具体过程如下:

第一次消元:


1111 1 1 1 1

第二次消元:

0 0

与普通高斯消元相同,当最后结果为0时,方程有解,反之无解。略有不同的是,由于未知数的取值只能是0或1,所以在有自由元的时候,解的个数也是有限的,等于 2 2 自 由 元 个 数

代码
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int M=55;
bool x[M][M],hh[M][M];
int n,m,q;
void in()
{
    int a,b;
    memset(x,0,sizeof(x));
    memset(hh,0,sizeof(hh));
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;++i)
    {
        scanf("%d",&a);
        for(int j=1;j<=a;++j)
        scanf("%d",&b),hh[b][i]=1;
    }
    scanf("%d",&q);
}
void reset()
{
    for(int i=1;i<=n;++i)
    for(int j=1;j<=m;++j)
    x[i][j]=hh[i][j];
}
ll gauss()
{
    int pos,cot=0,p=1;
    for(int i=1;i<=m;++i)
    {
        pos=-1;
        for(int j=p;j<=n;++j)
        if(x[j][i]){pos=j;break;}
        if(pos<0){cot++;continue;}
        swap(x[p],x[pos]);
        for(int j=1;j<=n;++j)
        {
            if(x[j][i]&&j!=p)
            for(int k=i;k<=m+1;++k)
            x[j][k]^=x[p][k];
        }
        p++;
    }
    for(int i=p;i<=n;++i)
    if(x[i][m+1])return 0;
    return 1ll<<cot;
}
void ac(int p)
{
    int a;
    printf("Case %d:\n",p);
    for(int i=1;i<=q;++i)
    {
        reset();
        for(int j=1;j<=n;++j)
        scanf("%d",&x[j][m+1]);
        printf("%lld\n",gauss());
    }
}
int main()
{
    int T;
    scanf("%d",&T);
    for(int i=1;i<=T;++i)
    in(),ac(i);
    return 0;
}
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ShadyPi

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值