Switches

博客主要介绍了ACM-ICPC亚洲首尔区域赛中的一道题目J.Switches。题目要求找到一组二进制数,通过两两异或能得出从2^n-1到2^0的所有数。博主提供了详细的解决方案,包括线性基的概念和算法实现,使用C++编程语言,并讨论了如何处理二进制数过大无法直接异或的问题。
摘要由CSDN通过智能技术生成

2020-2021 ACM-ICPC, Asia Seoul Regional Contest

J. Switches

题目链接

题目模型:
给定一个有 n n n 个二进制长度为 1-500 数字的集合,我们希望能够找到 n n n 个子集,这 n n n 个子集中的数字两两异或可以分别得到 2 n − 1 , 2 n − 2 , . . . , 2 0 2^{n-1}, 2^{n-2}, ..., 2^{0} 2n1,2n2,...,20。让我们按顺序输出这 n n n 个子集。当然,如果不存在答案输出-1

题目分析下来也就是线性基能否异或得到某个数,板子题。唯一不同的就是二进制数过大不能直接异或,需要数组手动异或。另外需要记录得到每个向量需要的原向量标号。

#include<bits/stdc++.h>

using namespace std;
typedef long long ll;
int read() {
    int x = 0, f = 1; char ch = getchar();
    while(ch < '0' || ch > '9') { if(ch == '-') f = -f; ch = getchar(); }
    while(ch >= '0' && ch <= '9') { x = x * 10 + ch - '0'; ch = getchar(); }
    return x * f;
}
const int maxN = 505;
const int maxBit = 505;
int n;
int mp[maxN][maxN];
int p[maxBit][maxN]; //基向量
vector<int> vt[maxBit]; //记录每个基向量是由哪几个向量异或得到的
void add(int x) { //将mp[x]插入
    int tnum[n + 1];
    memset(tnum, 0, sizeof(tnum));
    tnum[x + 1] = 1;
    for(int i = 0; i < n; ++ i ) {
        if(mp[x][i]) {
            if(vt[i].empty()) {
                //p[i] = mp[x]
                for(int j = 0; j < n; ++ j ) p[i][j] = mp[x][j];
                for(int j = 1; j <= n; ++ j ) if(tnum[j]) vt[i].push_back(j);
                break;
            }
            //mp[x]异或p[i]
            for(int j = i; j < n; ++ j ) mp[x][j] ^= p[i][j];
            int siz = vt[i].size();
            for(int j = 0; j < siz; ++ j ) {
                tnum[vt[i][j]] ^= 1;
            }
        }
    }
}

int tar[maxBit];
vector<int>ans[maxN];
bool Zero(int i) {
    int ans = 0;
    for(; i < n; ++ i) {
        ans += tar[i];
    }
    return ans == 0;
}
bool judge(int x) { //判断tar能不能由线性基得到
    int tnum[n + 1];
    memset(tnum, 0, sizeof(tnum));
    for(int i = 0; i < n; ++ i ) {
        if(tar[i]) {
            //tar异或p[i]
            for(int j = i; j <= n; ++ j ) tar[j] ^= p[i][j];
            int siz = vt[i].size();
            for(int j = 0; j < siz; ++ j ) {
                tnum[vt[i][j]] ^= 1;
            }
            if(Zero(i)) {
                for(int j = 1; j <= n; ++ j ) if(tnum[j]) ans[x].push_back(j);
                return true;
            }
        }
    }
    return false;
}
int main() {
    n = read();
    for(int i = 0; i < n; ++ i ) {
        for(int j = 0; j < n; ++ j ) {
            mp[i][j] = read();
        }
        add(i);
    }
    bool ok = true;
    for(int i = 0; i < n; ++ i ) {
        tar[i] = 1;
        if(i) tar[i - 1] = 0;
        if(!judge(i)) {
            ok = false;
            break;
        }
    }
    if(!ok) {
        puts("-1");
    } else {
        for(int i = 0; i < n; ++ i ) {
            int siz = ans[i].size();
            for(int j = 0; j < siz; ++ j ) {
                printf("%d ", ans[i][j]);
            }
            puts("");
        }
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值