其他分享
首页 > 其他分享> > 线性基总结

线性基总结

作者:互联网

知识

这个好像没啥知识,就是对一个序列的异或操作进行一个简化

找到一个只有log级别的基

然后减小时间复杂度

例题

元素

题面

这个裸的线性基,直接排序从大到小加入,能加上的就加贡献,加不上的就算了

code
#include<bits/stdc++.h>
using namespace std;
#define re register int
#define ll long long
const int N=1005;
int n,ans;
struct node{
	ll id;int val;
	node(){}
	bool operator < (node a)const{
		return val>a.val;
	}
}sca[N];
ll lba[65];
bool ins(ll x){
	for(re i=60;i>=0;i--){
		if(x&(1ll<<i)){
			if(lba[i])x^=lba[i];
			else{
				lba[i]=x;
				return true;
			}
		}
	}
	return false;
}
signed main(){
	scanf("%d",&n);
	for(re i=1;i<=n;i++)scanf("%lld%d",&sca[i].id,&sca[i].val);
	sort(sca+1,sca+n+1);
	for(re i=1;i<=n;i++)if(ins(sca[i].id))ans+=sca[i].val;
	printf("%d",ans);
}

[bzoj2844]albus就是要第一个出场

这个有一个小结论,设n为原序列大小,k为线性基大小

那么线性基中可以得到的数,在原序列的异或中出现了\(2^{n-k}\)

因为,没有加入线性基的数异或上当前线性基表示的数,最后都可以在线性基中有唯一对应

code
#include<bits/stdc++.h>
using namespace std;
#define re register int
#define ll long long
const int mod=10086;
int n,a[100005];
int lba[35],ans;
void ins(int x){
	for(re i=30;i>=0;i--){
		if(!(x>>i&1))continue;
		if(lba[i])x^=lba[i];
		else {lba[i]=x;break;}
	}
}
int ksm(int x,int y){
	int ret=1;
	while(y){
		if(y&1)ret=ret*x%mod;
		x=x*x%mod;y>>=1;
	}
	return ret;
}
signed main(){
	scanf("%d",&n);
	for(re i=1;i<=n;i++)scanf("%d",&a[i]),ins(a[i]);
	int sum=0,q;scanf("%d",&q);
	for(re i=0;i<=30;i++){
		if(!lba[i])continue;
		if(q>>i&1)ans+=1<<sum;
		sum++;
	}
	printf("%lld",(1ll*ans*ksm(2,n-sum)%mod+1)%mod);
}

[bzoj3811]玛里苟斯

这个题明明就是测试点分治,分别对于1,2,{3,4,5} 分类

1的直接算,概率是\(\frac{1}{2}\)

2的直接按位乘就行了,最后除以2或者4

3以上的,直接线性基,然后暴力算

最后一定是x+0.5,证明在这篇博客

code
#include<bits/stdc++.h>
using namespace std;
#define re register int
#define ll unsigned long long
const int N=100005;
ll n,m,a[N],b[N],cnt;
ll lba[25],ans,mx,res;
void ins(ll x){
	for(re i=22;i>=0;i--){
		if(!(x>>i&1))continue;
		if(lba[i])x^=lba[i];
		else {lba[i]=x;break;}
	}
}
void sol1(){
	for(re i=1;i<=n;i++)ans|=a[i];
	printf("%llu",ans>>1);
	if(ans&1)printf(".5");
}
void sol2(){
	for(re i=1;i<=n;i++)mx|=a[i];
	for(re i=62;i>=0;i--){
		for(re j=62;j>=0;j--){
			if(!((mx>>i)&1)||!((mx>>j)&1))continue;
			bool flag=true;
			for(re k=1;k<=n;k++)if((a[k]>>i&1)!=(a[k]>>j&1)){flag=false;break;}
			if(flag)ans+=(1ull<<i+j)>>1,res+=(1ull<<i+j)&1;
			else ans+=(1ull<<i+j)>>2,res+=(1ull<<i+j)%4>0;
		}
	}
	//cout<<ans<<" "<<res<<endl;
	ans+=res>>1;printf("%llu",ans);
	if(res&1)printf(".5");
}
void sol3(){
	for(re i=1;i<=n;i++)ins(a[i]);
	for(re i=22;i>=0;i--)b[++cnt]=lba[i];//cout<<i<<" "<<cnt<<endl;
	ll mask=(1ull<<cnt)-1;
	for(re s=1;s<=mask;s++){
		ll tmp=0;
		for(re i=1;i<=cnt;i++)if((s>>i-1)&1)tmp^=b[i];
		ll x=0,y=1;
		for(re i=1;i<=m;i++)x*=tmp,y*=tmp,x+=y>>cnt,y&=mask;
		ans+=x;res+=y;ans+=res>>cnt;res&=mask;
	}
	printf("%llu",ans);if(res)printf(".5");
}
signed main(){
	scanf("%llu%llu",&n,&m);
	for(re i=1;i<=n;i++)scanf("%llu",&a[i]);
	if(m==1)sol1();
	if(m==2)sol2();
	if(m>=3)sol3();
}

[bzoj3569]DZY Loves Chinese II

虽然我一眼就看出来了怎么做,但是如果我不知道这个是线性基的题的话...

直接构造边之间的关系,让树边是所有覆盖它的非树边的异或和

这样如果全部都断了,那就不能全插入到线性基中

至于权值如何得到,直接rand

code
#include<bits/stdc++.h>
using namespace std;
#define re register int
const int N=1e5+5;
const int M=5e5+5;
int n,m,val[M],wal[N];
int to[M*2],nxt[M*2],head[N],rp;
void add_edg(int x,int y){
	to[++rp]=y;
	nxt[rp]=head[x];
	head[x]=rp;
}
int dfn[N],cnt;
void dfs(int x,int f){
	dfn[x]=++cnt;
	for(re i=head[x];i;i=nxt[i]){
		int y=to[i];
		if(y==f)continue;
		if(!dfn[y]){
			dfs(y,x);
			val[i+1>>1]=wal[y];
			wal[x]^=wal[y];
		}
		else if(dfn[y]<dfn[x]){
			val[i+1>>1]=rand();
			wal[x]^=val[i+1>>1];
			wal[y]^=val[i+1>>1];
		}
	}
}
int lba[35];
bool ins(int x){
	for(re i=31;i>=0;i--){
		if(!((x>>i)&1))continue;
		if(lba[i])x^=lba[i];
		else {lba[i]=x;return true;}
	}
	return false;
}
signed main(){
	srand(time(NULL));
	scanf("%d%d",&n,&m);
	for(re i=1,x,y;i<=m;i++){
		scanf("%d%d",&x,&y);
		add_edg(x,y);add_edg(y,x);
	}
	dfs(1,0);int lans=0;
	//for(re i=1;i<=m;i++)cout<<val[i]<<endl;
	int q;scanf("%d",&q);
	for(re i=1;i<=q;i++){
		int k,flag=1;scanf("%d",&k);
		memset(lba,0,sizeof(lba));
		while(k--){
			int x;scanf("%d",&x);x^=lans;
			if(!ins(val[x]))flag=0;
		}
		lans+=flag;
		if(flag)printf("Connected\n");
		else printf("Disconnected\n");
	}
}

[bzoj4184]shallot

这个好像是带修的线性基,我没看懂在线修改的,所以只能写离线的

直接分治就好了,网上说是CDQ分治可是我觉得好像不像吧

然后就是求最大值,直接疯狂异或就完事了

code
#include<bits/stdc++.h>
using namespace std;
#define re register int
#define ll long long
const int N=5e5+5;
ll n,a[N];
struct LBA{
	ll cnt,lba[65];
	void ins(ll x){
		for(re i=60;i>=0;i--){
			if(!((x>>i)&1))continue;
			if(lba[i])x^=lba[i];
			else {lba[i]=x;break;}
		}
	}
	ll query(){
		ll ret=0;
		for(re i=60;i>=0;i--)
			if((ret^lba[i])>ret)
				ret^=lba[i];
		return ret;
	}
}dct[100];
ll st[N],en[N],ans[N];
map<ll,ll> fa,ct;
void cdq(int l,int r,int x){
	if(l==r){
		if(a[l]>0)dct[x].ins(a[l]);
		ans[l]=dct[x].query();
		return ;
	}
	int mid=l+r>>1;
	dct[x+1]=dct[x];
	for(re i=mid+1;i<=r;i++)
		if(a[i]<0&&st[i]<=l)
			dct[x+1].ins(-a[i]);
	cdq(l,mid,x+1);
	dct[x+1]=dct[x];
	for(re i=l;i<=mid;i++)
		if(a[i]>0&&en[i]>r)
			dct[x+1].ins(a[i]);
	cdq(mid+1,r,x+1);
	return ;
}
signed main(){
	scanf("%lld",&n);
	for(re i=1;i<=n;i++){
		scanf("%lld",&a[i]);
		if(a[i]>0){
			ct[a[i]]++;
			if(ct[a[i]]==1)fa[a[i]]=i;
			else a[i]=0;
		}
		else{
			ct[-a[i]]--;
			if(ct[-a[i]]==0){
				st[i]=fa[-a[i]];
				en[fa[-a[i]]]=i;
			}
			else a[i]=0;
		}
	}
	for(re i=1;i<=n;i++)if(a[i]>0&&!en[i])en[i]=n+1;
	cdq(1,n,0);
	for(re i=1;i<=n;i++)printf("%lld\n",ans[i]);
}

[bzoj2322]梦想封印

这个是不停的删边,那么我们反着加回来,

将环放到线性基中,链用set维护就好了

别忘了在线性基中异或完了在放进去

code
#include<bits/stdc++.h>
using namespace std;
#define re register int
#define int long long
const int N=5005;
const int M=20005;
int n,m,q;
struct edge{
	int x,y,v;
}edg[M];
int id[M],val[N],ans[M];
bool vie[M],lt[M];
set<int> st,bin;
vector<int> vec[N];
int lba[N],cba=1;
void extend(int);
void update(int x){
	for(re i=60;i>=0;i--)if(((x>>i)&1)and(lba[i]))x^=lba[i];
	st.insert(x);
}
void rebuild(){
	bin=st;st.clear();
	while(bin.begin()!=bin.end()){
		update(*bin.begin());
		bin.erase(bin.begin());
	}
}
void ins(int x){
	for(re i=60;i>=0;i--){
		if(!((x>>i)&1))continue;
		if(lba[i])x^=lba[i];
		else {
			lba[i]=x;
			cba<<=1ll;
			rebuild();
			return ;
		}
	}
}
void add(int id){
	if(!lt[edg[id].x]&&!lt[edg[id].y]){
		vec[edg[id].x].push_back(id);
		vec[edg[id].y].push_back(id);
		return ;
	}
	if(lt[edg[id].x]&&lt[edg[id].y]){
		ins(val[edg[id].x]^val[edg[id].y]^edg[id].v);
		return ;
	}
	if(lt[edg[id].x])swap(edg[id].x,edg[id].y);
	lt[edg[id].x]=true;
	val[edg[id].x]=val[edg[id].y]^edg[id].v;
	update(val[edg[id].x]);
	extend(edg[id].x);
}
void extend(int x){for(re i=0;i<vec[x].size();i++)add(vec[x][i]);}
int ask(){
	return *st.end()*cba-1;
}
signed main(){
	scanf("%lld%lld%lld",&n,&m,&q);
	for(re i=1;i<=m;i++)scanf("%lld%lld%lld",&edg[i].x,&edg[i].y,&edg[i].v);
	for(re i=1;i<=q;i++)scanf("%lld",&id[i]),vie[id[i]]=1;
	lt[1]=1;st.insert(0);
	for(re i=1;i<=m;i++)if(!vie[i])add(i);
	ans[q+1]=ask();
	for(re i=q;i>=1;i--){
		add(id[i]);
		ans[i]=ask();
	}
	for(re i=1;i<=q+1;i++)printf("%lld\n",ans[i]);
}

幸运数字

题面

这个题好说直接点分治,这个好象是除了第一题以外最简单的吧

code
#include<bits/stdc++.h>
using namespace std;
#define re register int
#define ll long long
const int N=20005;
ll lba[N][65];
int n,m;
ll val[N],ans[N*10];
int to[N*2],nxt[N*2],head[N],rp;
int rt,mx,ms[N],siz[N];
bool vis[N];
vector<pair<int,int> > qus[N];
void add_edg(int x,int y){
	to[++rp]=y;
	nxt[rp]=head[x];
	head[x]=rp;
}
void get_rt(int x,int f,int sz){
	siz[x]=1;ms[x]=0;
	for(re i=head[x];i;i=nxt[i]){
		int y=to[i];
		if(vis[y]||y==f)continue;
		get_rt(y,x,sz);
		siz[x]+=siz[y];
		ms[x]=max(ms[x],siz[y]);
	}
	ms[x]=max(ms[x],sz-siz[x]);
	if(ms[x]<mx)mx=ms[x],rt=x;
}
int bl[N],son[N],cnt;
void ins(int id,ll x){
	for(re i=60;i>=0;i--){
		if(!(x>>i&1))continue;
		if(lba[id][i])x^=lba[id][i];
		else {lba[id][i]=x;break;}
	}
}
ll query(ll x){
	for(re i=60;i>=0;i--)
		if((x^lba[0][i])>x)
			x^=lba[0][i];
	return x;
}
void get_lba(int x,int f){
	ins(x,val[x]);
	son[++cnt]=x;
	for(re i=head[x];i;i=nxt[i]){
		int y=to[i];
		if(vis[y]||y==f)continue;
		memcpy(lba[y],lba[x],sizeof(lba[y]));
		get_lba(y,x);
	}
}
void sol(int x,int sz){
	vis[x]=1;bl[x]=x;//cout<<"now"<<x<<" "<<val[x]<<endl;
	memset(lba[x],0,sizeof(lba[x]));
	for(re i=head[x];i;i=nxt[i]){
		int f=to[i];if(vis[f])continue;
		memset(lba[f],0,sizeof(lba[f]));
		cnt=0;get_lba(f,0);//cout<<cnt<<endl;
		for(re j=1;j<=cnt;j++){
			int u=son[j];//cout<<"u "<<u<<endl;
			for(re k=0;k<qus[u].size();k++){
				int v=qus[u][k].first;
				if(bl[v]!=x)continue;
				//cout<<"sb "<<u<<" "<<v<<" ";
				memcpy(lba[0],lba[u],sizeof(lba[0]));
				for(re l=0;l<=60;l++)if(lba[v][l])ins(0,lba[v][l]);
				ans[qus[u][k].second]=max(query(0ll),query(val[x]));
				//cout<<query(0ll)<<" "<<query(val[x])<<endl;
			}
		}
		for(re j=1;j<=cnt;j++)bl[son[j]]=x;
	}
	for(re i=head[x];i;i=nxt[i]){
		int y=to[i];
		if(vis[y])continue;
		mx=0x3f3f3f3f;
		get_rt(y,0,siz[y]>siz[x]?sz-siz[x]:siz[y]);
		sol(rt,siz[y]>siz[x]?sz-siz[x]:siz[y]);
	}
}
signed main(){
	scanf("%d%d",&n,&m);
	for(re i=1;i<=n;i++)scanf("%lld",&val[i]);
	for(re i=1;i<n;i++){
		int x,y;scanf("%d%d",&x,&y);
		add_edg(x,y);add_edg(y,x);
	}
	for(re i=1;i<=m;i++){
		int x,y;scanf("%d%d",&x,&y);
		if(x==y)ans[i]=val[x];
		else{
			qus[x].push_back(make_pair(y,i));
			qus[y].push_back(make_pair(x,i));
		}
	}
	mx=0x3f3f3f3f;
	get_rt(1,0,n);
	//cout<<rt<<endl;
	sol(rt,n);
	for(re i=1;i<=m;i++)printf("%lld\n",ans[i]);
}

标签:总结,int,ll,lba,re,线性,--,void
来源: https://www.cnblogs.com/hzoi-fengwu/p/15192314.html