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

网上写的题解都好简略啊。。。
假设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.

你可能感兴趣的:(线性代数)