POJ1830开关问题

题目:http://poj.org/problem?id=1830

这道题中,用a[ i ]表示 i 是否操作,与它相关的点受到的影响就是被异或1一下(0^1=1,1^1=0,真是太美妙了)。

所以式子就是一边是常数,表示 i 的初状态和末状态一不一样;另一边是一堆a[ k ](含a[ i ],因对 i 的末状态有影响)的异或和;

  异或和要消,就是互相异或一下,也可以看成减,把 j 行的 i 的系数减没了。

异或真是很美妙。它既是不进位加法,又是不进位减法。

  用它高斯消元甚至都不用除,因为只有0和1。

用蓝皮书上的好方法,用一个int表示了那个二进制串,这样消别的行的时候整个异或一下就行了,不用每一位去异或。

  人家判断何时只剩自由元也很美妙。那个把最高位最大的行换过来很好(很正常?)。

int的二进制末位表示那个得数,这样1<<k就表示a[k]的系数,不用1<<(k-1)了。

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int lm=29;
int n,f[lm+5],ans,K;
void gauss()
{
    for(int i=1;i<=n;i++)//ans初值为1,表示正常从n退出则有1种方案 
    {
        int k=i;
        for(int j=i+1;j<=n;j++)
            if(f[j]>f[k])k=j;
        if(k!=i)swap(f[i],f[k]);
        if(f[i]==0){ans=(1<<(n-i+1));return;}
        if(f[i]==1){ans=0;return;}//末位是1,前面却没有系数了之不合法 
        for(int l=lm;l;l--)
            if(f[i]&(1<<l))
            {
                for(int j=1;j<=n;j++)
                    if(i!=j&&(f[j]&(1<<l)))f[j]^=f[i];
                break;
            }
    }
}
int main()
{
    scanf("%d",&K);
    while(K--)
    {
        memset(f,0,sizeof f);ans=1;
        scanf("%d",&n);int x,y;
        for(int i=1;i<=n;i++)scanf("%d",&f[i]);
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&x);f[i]^=x;
            f[i]|=(1<<i);
        }
        while(1)
        {
            scanf("%d%d",&x,&y);
            if(!x&&!y)break;
            f[y]|=(1<<x);//y会受x影响故 
        }
        gauss();
        if(ans==0)printf("Oh,it's impossible~!!\n");
        else printf("%d\n",ans);
    }
    return 0;
}
扫描二维码关注公众号,回复: 900491 查看本文章

猜你喜欢

转载自www.cnblogs.com/Narh/p/9052674.html