本文移植自个人的原博客(原文

POI2003 Sequences without Stammers

题目链接

BZOJ 2606

题目描述

第一行包含两个整数$N$和$M$,表示该无向图中点的数目与边的数目。 接下来$M$行描述$M$条边,每行三个整数$S_i,T_i,D_i$,表示$S_i$与$T_i$之间存在一条权值为 $D_i$的无向边。 图中可能有重边或自环。

仅包含一个整数,表示最大的$XOR$和(十进制结果),注意输出后加换行回车。

题解

裸线性基,曾经在CF上做过这题,大致思路就是将所有能遍历到的环插入线性基内,因为一定能通过经过一条路径绕环一圈再从这个路径回来的方式,使得答案异或上环的权值,那么任取一条$1$到$n$的路径到线性基中求异或最大值即可。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 2e5+1;

struct XOR{
ll a[63];
void add(ll x){
for(int i=62;i>=0;--i)
if((x>>i)&1)x^=a[i];
for(int i=62;i>=0;--i)
if(((x>>i)&1)&&!a[i]){
a[i]=x;
for(int j=i+1;j<=62;++j)
if((a[j]>>i)&1)a[j]^=x;
return;
}
}
ll sum(ll x){
for(int i=62;i>=0;--i)
if(((x>>i)&1)==0)x^=a[i];
return x;
}
}T;

int head[N],n,m;
bool vis[N];
ll d[N];

struct nd{
int ne,to;ll w;
}e[N*2];

void in(int x,int y,ll w){
static int cnt=0;
e[++cnt].to=y;e[cnt].ne=head[x];head[x]=cnt;e[cnt].w=w;
}

void dfs(int x,ll w){
vis[x]=1;
d[x]=w;
for(int i=head[x];i;i=e[i].ne){
int y=e[i].to;
if(vis[y])T.add(d[x]^d[y]^e[i].w);
else dfs(y,w^e[i].w);
}
}

int main(){
scanf("%d%d",&n,&m);
ll w;
for(int i=1,x,y;i<=m;++i)scanf("%d%d%lld",&x,&y,&w),in(x,y,w),in(y,x,w);
dfs(1,0);
printf("%lld\n",T.sum(d[n]));
}

BZOJ2844 albus就是要第一个出场

题目链接

BZOJ 2844

题解

此题实际上为求$k$小线性基的对偶问题,如不考虑重复的话,直接二分皆可解决。考虑上重复的问题,不妨设$cnt$为线性基底个数,那么显然不重复的异或值只有$2^{cnt}$中,即有$n−cnt$个线性无关变量,即每一种异或值都可以经过2n−cnt种变换仍不改变,即每一种异或值共重复了$2^{n−cnt}$次,去重二分后计算上重复次数即可得到答案。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e5+1;
const ll mod = 10086;
int n,q[N],Q,cnt;

struct Xor{
int a[N],b[N];
void add(int x){
for(int i=31;i>=0;--i)
if((x>>i)&1)x^=a[i];
for(int i=31;i>=0;--i)
if(((x>>i)&1)&&!a[i]){
a[i]=x;
for(int j=i+1;j<=31;++j)
if((a[j]>>i)&1)a[j]^=x;
return;
}
}
void init(){
cnt=0;
for(int i=0;i<=31;++i)
if(a[i])b[++cnt]=a[i];
}
int kth(int k){
if(k>(1<<cnt))return 2e9;
int ret=0;
for(int i=1;i<=cnt;++i)
if((k>>(i-1))&1)ret^=b[i];
return ret;
}
}T;

ll qpow(ll a,ll b){
ll ret=1;
while(b){
if(b&1)ret=ret*a%mod;
b>>=1;
a=a*a%mod;
}
return ret;
}

int main(){
scanf("%d",&n);
for(int i=1;i<=n;++i)scanf("%d",&q[i]),T.add(q[i]);
scanf("%d",&Q);
T.init();
int l=0,r=(1<<cnt)-1;
while(l!=r){
int mid=l+r>>1;
if(T.kth(mid)>=Q)r=mid;
else l=mid+1;
}
printf("%d",(1ll+qpow(2,n-cnt)*(ll)l)%mod);
}
/*
3
1 2 3
3
*/

CQOI2013 新Nim游戏

题目链接

BZOJ 3105

题解

对于后手玩家来说,只要存在多个基底异或值为零,即可将其余所有火柴删除,使得所有最后普通$nim$游戏有异或值为$0$,即后手胜。那么先手玩家为了避免这种情况需要删除所以可能导致异或值为$0$的情况,即可能有线性无关变量的情况(易知不存在先手必败的情况),那么如何将第一个人删除的尽可能少,可以将所有火柴排个序,从大到小插入线性基中,如果为线性无关变量,则直接统计进答案即可。(具体证明需要考虑拟阵,暂时没学)(现在学了,还是不会证)

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e5+1;
int n,a[N];
struct Xor{
int a[32];
bool add(int x){
for(int i=31;i>=0;--i)
if((x>>i)&1)x^=a[i];
for(int i=31;i>=0;--i)
if(((x>>i)&1)&&!a[i]){
a[i]=x;
for(int j=i+1;j<=31;++j)
if((a[j]>>i)&1)a[j]^=x;
return 1;
}
return 0;
}
}T;

int main(){
scanf("%d",&n);
for(int i=1;i<=n;++i)scanf("%d",&a[i]);
sort(a+1,a+n+1);
ll ans=0;
for(int i=n;i>=1;--i)if(!T.add(a[i]))ans+=a[i];
printf("%lld",ans);
}
/*
6
5 5 6 6 5 5
*/

SCOI2016 幸运数字

题目链接

BZOJ 4568

题解

该题实际上就是将$x$到$y$中所有权值插入线性基中求异或最大值即为答案,观察$n<=20000$的性质,可以通过倍增维护,每次查询启发式合并即可。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 2e4+1;

int read(){
int x=0;char ch=getchar();
while(ch<'0'||ch>'9')ch=getchar();
while(ch>='0'&&ch<='9')x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
return x;
}

struct Xor{
ll a[61];
void add(ll x){
for(int i=60;i>=0;--i)
if((x>>i)&1)x^=a[i];
if(!x)return;
for(int i=60;i>=0;--i)
if(((x>>i)&1)&&!a[i]){
a[i]=x;
for(int j=i+1;j<=60;++j)
if((a[j]>>i)&1)a[j]^=x;
return;
}
}
void init(){
memset(a,0,sizeof(a));
}
ll mx(){
ll ret=0;
for(int i=60;i>=0;--i)
if(((ret>>i)&1)==0)ret^=a[i];
return ret;
}
}g[N][15],R;

Xor mix(Xor a,Xor b){
Xor c;c.init();
int cnta=0,cntb=0;
for(int i=0;i<=60;++i)if(a.a[i])cnta++;
for(int i=0;i<=60;++i)if(b.a[i])cntb++;
if(cnta>=cntb){
for(int i=0;i<=60;++i)c.a[i]=a.a[i];
for(int i=0;i<=60;++i)
if(b.a[i])c.add(b.a[i]);
}
else{
for(int i=0;i<=60;++i)c.a[i]=b.a[i];
for(int i=0;i<=60;++i)
if(a.a[i])c.add(a.a[i]);
}
return c;
}

int head[N],cnt,n,m,Q,f[N][15],d[N],fa[N];
ll a[N];
struct nd{
int to,ne;
}e[N*2];

void in(int x,int y){
e[++cnt].to=y;e[cnt].ne=head[x];head[x]=cnt;
}

void dfs(int x){
for(int i=head[x];i;i=e[i].ne)
if(e[i].to!=fa[x]){
int y=e[i].to;
fa[y]=x;
f[y][0]=x;g[y][0].add(a[x]);
d[y]=d[x]+1;
dfs(y);
}
}

void pre(){
for(int j=1;j<15;++j)
for(int i=1;i<=n;++i){
f[i][j]=f[f[i][j-1]][j-1];
g[i][j]=mix(g[f[i][j-1]][j-1],g[i][j-1]);
}
}

int lca(int x,int y){
R.init();
if(d[x]<d[y])swap(x,y);
for(int i=14;i>=0;--i)
if(d[f[x][i]]>=d[y]){
R=mix(R,g[x][i]);
x=f[x][i];
}
if(x==y)return x;
for(int i=14;i>=0;--i)
if(f[x][i]!=f[y][i]){
R=mix(R,g[x][i]);R=mix(R,g[y][i]);
x=f[x][i],y=f[y][i];
}
R.add(a[f[x][0]]);
return f[x][0];
}

ll solve(int x,int y){
// cout<<x<<" "<<y<<endl;
int l=lca(x,y);
R.add(a[x]);R.add(a[y]);
return R.mx();
}

int main(){
n=read();Q=read();
for(int i=1;i<=n;++i)scanf("%lld",&a[i]);
for(int i=1,x,y;i<n;++i)x=read(),y=read(),in(x,y),in(y,x);
d[1]=1;dfs(1);
pre();
for(int i=1,x,y;i<=Q;++i)printf("%lld\n",solve(read(),read()));
}
/*
4 2
11 5 7 9
1 2
1 3
1 4
2 3
1 4
*/