题目链接: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;
}