JK_LOVER
2020-04-29 17:24:30
若格式炸了来这里博客
用一个权值大小为
多维向量的作为基底的特殊形式,可用高斯消元理解。(关于实质下文用题专门讲解)
A,B,C,D四个不可被相互表达的元素,但
因为
而又因为
仍可用异或的交换率来解释,本人感觉这条和性质2是等价的。
终于到了正题,我们先来了解一下线性基的构建。
因为线性基要满足
在每次插入一个数时由高到低枚举二进制位,当这个元素此位是一时,若此位已经被插入就用它异或该位的元素,这样是为了满足
否则就将该元素放入,然后退出。
void insert(int x)//插入线性基
{
for(int i = 63;i>=0;i--)
{
if(x&(1LL<<i))
{
if(!p[i])
{
p[i]=x;
return;
}
x^=p[i];
}
}
}
当我们已经构建完线性基之后,我们就可以利用性质来实现一些算法了。
考虑最大异或最大值一定是尽量让最高位为1的元素,就可以考虑以下贪心:由高到低枚举线性基中的元素,如果此位为0,那么异或后一定更优,如果为1,那么异或后一定不会更优。
for(LL i = 62;i >= 0;i--)
{
if((ans>>i)&1)continue;
else
ans^=p[i];
}
或者直接这样写
for(LL i = 62;i >= 0;i--)
{
if((p[i]^ans)>ans)
{
ans^=p[i];
}
}
都是可以的。
考虑最大异或最小值一定是尽量让最高位为0的元素,这不就是线性基最低位的性质嘛,那就直接输出最小值就好了。这就错了,我们在下定义时是将
int Min()//最小
{
if(pd==1) return 0;//如果可以构造出0,这里需要特判
for(int i = 0;i <= 62;i++)
if(p[i]) return p[i];
}
这里要用以下性质:
为了实现更多的功能,我们必须对原线性基改造。
经过这个函数,我们可以使线性基有一个非常优秀的性质:
void rebuild()//重构线性基
{
for(int i = 63;i >= 0;i--)
for(int j = i-1;j >= 0;j--)
if(p[i]&(1LL<<j)) p[i]^=p[j];
for(int i = 0;i <= 63;i++) if(p[i]) d[cnt++]=p[i];
}
作用显然是把一个高位的线性基的其它位变得更小,那又有什么用呢?
我们考虑用证明:
我们可以证明一个首项为
上式满足
从而可得
又因为一个
有了这个性质我们就可以轻易的写出查询第
int kth(int k)//第k大
{
if(k >= (1LL<<cnt)) return -1;
int ans=0;
for(int i = 62;i>=0;i--)
{
if((k>>i)&1) ans^=d[i];
}
return ans;
}
P3812 【模板】线性基 模板题,正常思路即可。
#include<bits/stdc++.h>
using namespace std;
#define LL long long
LL read()
{
LL x=0,f=0;char ch=getchar();
while(!isdigit(ch)){if(ch=='-')f=1;ch=getchar();}
while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
return f?-x:x;
}
LL n,ans,p[1919191];
void work(LL x)
{
for(LL i = 62;i >= 0;i--)
{
if((1LL<<i)&x)
{
if(!p[i]) {
p[i] =x;
return ;
}
x^=p[i];
}
}
}
int main()
{
n=read();
while(n--)
work(read());
for(LL i = 62;i >= 0;i--)
{
if((ans>>i)&1)continue;
else
ans^=p[i];
}
cout<<ans<<endl;
}