题目: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~!! "); else printf("%d ",ans); } return 0; }