[BZOJ2844]albus就是要第一个出场 高斯消元+线性基

来源:互联网 发布:红蚂蚁网络销售 编辑:程序博客网 时间:2024/05/22 19:32

网上写的题解都好简略啊。。。
假设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.
0 0
原创粉丝点击