版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/Ronaldo7_ZYB/article/details/89816875
题目描述
有N个相同的开关,每个开关都与某些开关有着联系,每当你打开或者关闭某个开关的时候,其他的与此开关相关联的开关也会相应地发生变化,即这些相联系的开关的状态如果原来为开就变为关,如果为关就变为开。
你的目标是经过若干次开关操作后使得最后N个开关达到一个特定的状态。
对于任意一个开关,最多只能进行一次开关操作。
你的任务是,计算有多少种可以达到指定状态的方法。(不计开关操作的顺序)
题解
我们设 表示第 个开关变动的时候第 个开关是否变动, 表示不变。
设
表示起始状态,
表示终止状态。则有一下异或方程:
显然异或是不进位的加法,我们可以直接进行异或高消。
现在考虑如何进行方案数的计算:显然在高斯消元的时候,假如在消第i列时,如果此时这一位后面的每一个数都是 的话,无论这些数的取值多少都是不必要的。若这一位之后又 个数都为 ,则当前对答案的贡献就是 。若出现0=1的情况,则答案无解。
注意:在枚举每一列时只能统计一个数的方案;因为其他的某些数字可能由 变为非 数。
其实我们在这里总结一下:在每一次高斯消元的过程中,若出现系数全为 ,常数不为 的情况,则方程无解;若出现系数和常数都为 的情况,则有多组解。
代码如下:
#include <bits/stdc++.h>
using namespace std;
int n;
int a[100];
void init(void)
{
cin>>n;
for (int i=1,k;i<=n;++i)
{
cin>>k;
a[i] ^= k;
a[i] |= (1<<i);//a[i][i] = 1
}
for (int i=1,k;i<=n;++i)
{
cin>>k;
a[i] ^= k;
}
int x,y;
while (cin>>x>>y)
{
if (!x && !y) break;
a[y] |= (1<<x);//a[y][x] = 1
}
return;
}
int work(void)
{
init();
for (int i=1;i<=n;++i)
{
for (int j=i+1;j<=n;++j)
if (a[j]>a[i]) swap(a[i],a[j]);
if (a[i] == 1) return 0;//0 = 1,无解
if (a[i] == 0) return 1<<(n-i+1);//确定i-1个解
for (int k=n;k>=1;--k)//查找最高位的1的位置
{
if ((a[i]>>k)&1 == 1)
{
for (int j=1;j<=n;++j)
if (i^j && (a[j]>>k)&1 == 1)//如果当前位=1则需要消元
a[j] ^= a[i];
break;
}
}
}
return 1;
}
int main(void)
{
freopen("switch.in","r",stdin);
freopen("switch.out","w",stdout);
int T;
cin>>T;
while (T --)
{
memset(a,0,sizeof(a));
int ans = work();
if (ans == 0) cout<<"Oh,it's impossible~!!\n";
else cout<<ans<<endl;
}
return 0;
}