codeforces-div1-285-D

题目链接:Misha and XOR


解法:

首先对于每个数, 先讲其转换为二进制, 转换的方法就是模拟大数除法, 转化的时候最好压缩几位, 这样速度快些.

然后问题其实就变成了, 对于某个二进制数, 能否用出现在它之前的那些二进制数表示.

有点经验的话, 就很容易发现需要用高斯消元法来解决.

但是高斯消元法的复杂度是m^3(m为矩阵大小), 这里一共有n个数, 复杂度就是n*m^3, 对于这里的数据, 明显超时了.

由于这些数是按照顺序加入的, 每加入一个数就询问能否由前面的数组成, 这样就可以根据这个顺序性, 每次加入数过后, 维护一些东西, 以求降低复杂度.


假如现在加入到了第k个数, 那么我们就维护0~k个数构成的矩阵, 转化成阶梯形式过后, 每行现在的值, 每行第一个非0元素的位置, 以及每行被哪些行异或过, 有些拗口, 下面有个例子.

比如第一个元素为 1100(十进制的3), 把这个元素加入过后, 我们维护了第一行的这些东西: 这一行的值为1100, 第一个非0元素在第0位, 构成这一行的数有第1个数;

现在第二个元素为 1000(十进制的2), 先把这个元素加到矩阵的第二行, 于是现在的矩阵为

1100

1000 

现在把他变为阶梯矩阵, 

1100

0100

现在再来维护第二行的东西: 这一行值为0100, 第一个非0元素在第1位, 构成这一行的数有第1, 2个数(因为这一行实际上是有第一二个数异或得来)


现在考虑一下询问, 

假如第三个数是1000(1), 我们就可以根据刚才维护的阶梯矩阵来求解他的值, 

首先我们看他第0位是1, 因此我们需要把它和第一行进行异或来抵消这个1, 现在它的值就变成了0100, 

然后我们看它第1位是1, 因此我们需要把它和第二行进行异或来抵消这个1, 现在它是0000, 

发现它现在是0000, 就说明它能够由之前的数字构成了.

如果异或完后的值不等于0, 那么说明它不能由之前数字构成.


下面是代码

#include <stack>
#include <stdio.h>
#include <iostream>
#include <string.h>
#include <vector>
#include <math.h>
#include <queue>
#include <map>
#include <set>
#include <algorithm>
#include <bitset>
using namespace std;

#define FOR(i, j, k) for(int i=(j);i<=(k);i++)
#define REP(i, n) for(int i=0;i<(n);i++)
#define mst(x, y) memset(x, y, sizeof(x));
#define pii pair<int, int>
#define fr first
#define sc second
#define left myleft
#define right myright
#define ll long long
#define ull unsigned long long
#define seed 1331
#define eps 1e-5
#define pdd pair<double, double>
#define inf 1e9

const int lim = 2050;
int n, first[lim];
bitset <lim> row[lim], who[lim], x, ans;
char s[666];

void read(){scanf("%s", s);}
void convert(){
    x.reset();
    int len = strlen(s), pos = 0;
    REP(i, len) s[i] -= '0';
    while(len){
        ll base = (ll)1<<50, remain = 0;
        int p = 0;
        REP(i, len){
            ll sum = remain*10 + s[i];
            if(sum >= base || p)  s[p ++] = sum / base;
            remain = sum % base;
        }
        REP(i, 50) x[pos ++] = remain & 1, remain /= 2;
        len = p;
    }
}

int main(){
//    freopen("4", "r", stdin);
    cin>>n;
    REP(k, n){
        read();
        convert();

        ans.reset();
        ans.set(k);
        REP(i, k) if(x.test(first[i]))
            x ^= row[i], ans ^= who[i];
        if(x.any()){ // 如果不为0, 那么输出0, 且把这个数加入矩阵作为一行
            puts("0");
            REP(i, lim) if(x[i]) {first[k] = i;break;}
            row[k] = x;
            who[k] = ans;
            int p = k;
            while(p && first[p]<first[p-1]){
                swap(first[p], first[p-1]);
                swap(who[p], who[p-1]);
                swap(row[p], row[p-1]);
                p --;
            }
        }else{ // 如果为0, 那么直接输出答案
            printf("%d", ans.count()-1);
            REP(i, k) if(ans.test(i)) printf(" %d", i);
            puts("");
        }
    }
    return 0;
}



评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值