网上写的题解都好简略啊。。。
假设n个数一共消出了k个线性基。n个数能xor的所有数一共有2^n个(不去重),k个基能xor出的数一共有2^k个(本身就没有重复)。所以xor的值域中每个数都出现了2^(n-k)次。为什么呢?
很简单啊,消的时候是不改变xor出的所有数的,消出k个后面其实还剩下(n-k)个0啊。。。每个数都可以xor上一些0,不就是2^(n-k)个吗?!
现在我们要求比q小的有多少数了。
线性基还有一个性质:如果最高位的1在第x位的线性基不存在,那么xor集合中的数的第x位都是不能自己改变的(随着其他位的改变而改变)。
所以我们从高到低扫描线性基,若q的某一位为1,比它小的数这一位可能是0或1,是0的数共有2^(k-i)个,i表示当前扫到第几个线性基。如果是1,说明这个数肯定是xor上这个线性基得来的,所以直接xor上这个线性基,继续循环下去找。由于每个重复了2^(n-k)次,所以代码里直接乘了2^(n-i)。
这题要用高斯消元求线性基,高斯消元能保证每一个可以自由变动的位在所有线性基中只有一个1。
var
n,i,q,num,p,j:longint;
a,b:array[0..100100]of longint;
lb:array[0..40]of longint;
ans:int64;
const
md=10086;
procedure swap(var x,y:longint);
var
t:longint;
begin
t:=x;
x:=y;
y:=t;
end;
procedure gauss;
var
i,j,k:longint;
begin
num:=n;
for i:=1 to n do
begin
for j:=i+1 to n do
if a[j]>a[i] then swap(a[i],a[j]);
if a[i]=0 then begin num:=i-1; break; end;
for j:=31 downto 0 do
if ((a[i]>>j)and 1)=1 then
begin
b[i]:=j;
for k:=1 to n do
if (k<>i)and(((a[k]>>j)and 1)=1) then a[k]:=a[k] xor a[i];
break;
end;
end;
end;
function ksm(a,b:int64):int64;
begin
ksm:=1;
while b>0 do
begin
if b mod 2=1 then ksm:=ksm*a mod md;
a:=a*a mod md;
b:=b div 2;
end;
end;
begin
readln(n);
for i:=1 to n do
read(a[i]);
num:=0;
gauss;
for i:=1 to num do
outp(a[i]);
ans:=1;
readln(q);
for i:=1 to num do
if ((q>>b[i])and 1)=1 then
begin
q:=q xor a[i];
ans:=(ans+ksm(2,n-i))mod md;
end;
writeln(ans);
end.