AcWing 208. 开关问题(高斯消元 + 异或运算)

我们假设x_i是第i个开关的操作情况,x_i为1表示按了这个开关,x_i 为0表示没有按这个开关.再用a_{i, j}表示第i个开关和第j个开关有无联系,a_{i, j}=1表示第i个开关和第j个开关有联系,而a_{i, j}=0则表示没有联系,特别的,a_{i, i}=1.(因为进行一次操作就相当于1异或)

一个开关的最后的状态dst_i,取决于其初始状态src_i,以及所有跟他有联系的异或操作.列出异或方程组得:

 

异或就是不进位加法,可以写出增广矩阵,矩阵中每个数要么是1,要么是0.然后,在执行消元的时候就把加法减法替换成异或运算,而且不需要执行乘法操作(无进位).最终我们可以得到对应的简化梯形矩阵.则方程组无解,因为自由元可以取0或1,所以解的数量有2^{cnt},其中cnt为自由元的数量.

在编码时可以用状态压缩把矩阵压缩成二进制的方式表示和运算.其中第0位表示增广矩阵的最后一列常数,第1~n位表示增广矩阵第1~n的系数.由于这道题不需要我们按顺序求出每个开关的操作情况,只要求求方案数,所以我们不用担心顺序的问题.

代码如下:

#include <bits/stdc++.h>
// #define LOCAL
#define INF 0xf3f3f3f3f3f3f3f
#define IOS ios::sync_with_stdio(false), cin.tie(0)
#define int long long
#define debug(a) cout << #a << "=" << a << endl;
using namespace std;
#define vec vector<int>
#define mat vector<vec>
const int N = 50;
int a[N];

void solve(){
    int n;
    cin >> n;
    for (int i = 1; i <= n; ++i)
        cin >> a[i];
    for (int i = 1, x; i <= n; ++i){
        cin >> x;
        a[i] ^= x;
        a[i] |= 1 << i;//a[i][i] = 1
    }
    int x, y;
    while (cin >> x >> y && x && y){
        a[y] |= 1 << x; // a[y][x] = 1
    }
    int ans = 1;
    for (int i = 1; i <= n; ++i){
        /* 找出主元位数最高的一行,并换到最上面 */
        for (int j = i + 1; j <= n; ++j){
            if (a[j] > a[i])
                swap(a[j], a[i]);
        }
        /* 如果出现0=1的情况,说明无解 */
        if (a[i] == 1){
            ans = 0;
            break;
        }
        /* 如果主元全部被消除,当前自由元的个数为n - i + 1,有2^(n-i+1)个解 */
        if (a[i] == 0){
            ans = 1 << (n - i + 1);
            break;
        }
        /* 将最高位的除了最上面的数全部消除为0 */
        for (int k = n; k; --k)
            if (a[i] >> k & 1){
                for (int j = 1; j <= n; ++j)
                    if (i != j && (a[j] >> k & 1))
                        a[j] ^= a[i];
                break;
            }
    }
    if (ans == 0)
        cout << "Oh,it's impossible~!!\n";
    else
        cout << ans << '\n';
}
signed main(){
#ifdef LOCAL
    freopen("input.in", "r", stdin);
    freopen("output.out", "w", stdout);
#endif
    IOS;
    int k;
    cin >> k;
    while (k--){
        solve();
    }
    
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值