线性代数

P3812 【模板】线性基

模板题

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int D=64;
int n;
ll w[D];
bool insert(ll x){
	for(int i=D-1;i>=0;i--){
		if((x>>i)&1){
			if(w[i]) x^=w[i];
			else{
				w[i]=x;
				return true;
			}
		}
	}
	return false;
}
void simp(){
	for(int i=D-1;i>=0;i--){
		if(w[i]!=0){
			for(int j=D-1;j>i;j--){
				if((w[j]>>i)&1){
					w[j]^=w[i];
				}
			}
		}
	}
}
ll maxxxor(){
	ll ans=0;
	for(int i=D-1;i>=0;i--){
		if(w[i]) ans^=w[i];
	}
	return ans;
}
int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
		ll x;
		scanf("%lld",&x);
		insert(x);
	}
	simp();
	cout<<maxxxor();
}

P4570 [BJWC2011] 元素

  • 题意:

给你 n 个元素,每个元素有序号 a 和魔力 b,现在要选出一些元素,使它们的序号不存在一个非空子集异或和为零,且价值之和最大。

  • 思路:

将魔力 b 由大到小排序,遍历元素,每遍历到一个元素如果能插入线性基就把价值加上,否则就不加。

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int D=64;
ll w[D];
int n;
struct md{
	int b;
	ll a;
}z[1004];
bool insert(ll x){
	for(int i=D-1;i>=0;i--){
		if((x>>i)&1){
			if(w[i]) x^=w[i];
			else{
				w[i]=x;
				return true;
			}
		}
	}
	return false;
}
void simp(){
	for(int i=D-1;i>=0;i--){
		if(w[i]!=0){
			for(int j=D-1;j>i;j--){
				if(w[j]!=0) w[j]^=w[i];
			}
		}	
	}
}
bool cmp(md x,md y){
	return x.b>y.b;
}
ll ans;
int main(){
	cin>>n;
	for(int i=1;i<=n;i++){
		scanf("%lld%d",&z[i].a,&z[i].b);
	}
	sort(z+1,z+1+n,cmp);
	for(int i=1;i<=n;i++){
		if(insert(z[i].a)){
			ans+=z[i].b;
		}
		else continue;
	}
	cout<<ans;
}

P4151 [WC2011] 最大XOR和路径

  • 题意:

在一个图中找一条从 1n 异或和最大的路径

  • 思路:

可以证明这样的一条路径是由一条链加上一些环构成的,于是 dfs 找到所有环扔进线性基中,在随便找一条从 1n 的链的异或和为初始值就行了。

之所以是随便一条链,因为我们发现如果选择链 A 后还有更优的从 1n 的链 B,那么必然就会形成一个环,用链 A 异或上这个环就能得到链 B

#include<bits/stdc++.h>
#define ll long long
using namespace std;
int n,m;
struct node{
	int to,nxt;
	ll w;
}z[200004];
int cnt,h[500004];
void add(int x,int y,ll w){
	z[++cnt].to=y;
	z[cnt].nxt=h[x];
	z[cnt].w=w;
	h[x]=cnt;
}
int vis[500004];
ll dn[500004];
const int D=64;
ll w[D];
void insert(ll x){
	for(int i=D-1;i>=0;i--){
		if((x>>i)&1){
			if(w[i]){
				x^=w[i];
			}
			else{
				w[i]=x;
				return ;
			}
		}
	}
}
void dfs(int x,ll res){
	dn[x]=res,vis[x]=1;
	for(int i=h[x];i;i=z[i].nxt){
		int y=z[i].to;
		if(vis[y]){
			insert(res^z[i].w^dn[y]);
		}
		else{
			dfs(y,res^z[i].w);
		}
	}
}
ll qury(ll x){
	ll ans=x;
	for(int i=D-1;i>=0;i--){
		ans=max(ans,ans^w[i]);
	} 
	return ans;
}
int main(){
	cin>>n>>m;
	for(int i=1;i<=m;i++){
		int u,v;
		ll w;
		scanf("%d%d%lld",&u,&v,&w);
		add(u,v,w);
		add(v,u,w);
	}
	dfs(1,0);
	cout<<qury(dn[n]);
}

P3292 [SCOI2016] 幸运数字

  • 题意:

给你一棵树,给定 xy,找到从 xy 的路径上最大异或和。

  • 思路:

找到 xylca ,把从 xlca 路径上的线性基与从 ylca 路径上的线性基合并一下就行了。

为了让答案尽可能大,我们要把线性基往深处插入,所以在插入过程中判断下深度即可。

#include<bits/stdc++.h>
#define int long long
using namespace std;
int n,q;
struct edge{
	int to,nxt;
}z[400004];
const int D=64;
struct tree{
	int dep,fa,maxxson,son,top;
	int pos[D],w[D];
}l[100003];
int h[100003];
int cnt;
void add(int x,int y){
	z[++cnt].to=y;
	z[cnt].nxt=h[x];
	h[x]=cnt;
}
int a[300003];
void dfs(int x,int fa){
	l[x].fa=fa;
	l[x].dep=l[fa].dep+1;
	l[x].son=1;
	l[l[x].maxxson].son=-1;
	for(int i=D-1;i>=0;i--) l[x].pos[i]=l[fa].pos[i],l[x].w[i]=l[fa].w[i];
	int X=a[x],_x=x;
	for(int i=D-1;i>=0;i--){
		if((X>>i)&1){
			if(l[x].w[i]){
				if(l[_x].dep>l[l[x].pos[i]].dep)	swap(_x,l[x].pos[i]),swap(X,l[x].w[i]);
				X^=l[x].w[i];
			}
			else{
				l[x].w[i]=X;
				l[x].pos[i]=_x;
				break;
			}
		}
	}
	for(int i=h[x];i;i=z[i].nxt){
		int y=z[i].to;
		if(y==l[x].fa) continue;
		else{
			dfs(y,x);
			l[x].son+=l[y].son;
			if(l[y].son>l[l[x].maxxson].son){
				l[x].maxxson=y;
				l[l[x].maxxson].son=l[y].son;
			}
		}
	}
}
void Dfs(int x,int top){
	l[x].top=top;
	if(!l[x].maxxson) return ;
	Dfs(l[x].maxxson,top);
	for(int i=h[x];i;i=z[i].nxt){
		int y=z[i].to;
		if(y==l[x].fa||y==l[x].maxxson) continue;
		else{
			Dfs(y,y);
		}
	}
}
int base[D];
int LCA(int a,int b){
	while(l[a].top!=l[b].top){
		if(l[l[a].top].dep<l[l[b].top].dep) swap(a,b);
		a=l[l[a].top].fa;
	}
	if(l[a].dep>l[b].dep) swap(a,b);
	return a;
}
signed main(){
	cin>>n>>q;
	for(int i=1;i<=n;i++) {
		scanf("%lld",&a[i]);
	}
	for(int i=1;i<n;i++){
		int x,y;
		scanf("%lld%lld",&x,&y);
		add(x,y);
		add(y,x);
	}
	dfs(1,0);
	Dfs(1,1);
	while(q--){
		int x,y;
		cin>>x>>y;
		int lca=LCA(x,y);
		for(int i=D-1;i>=0;i--){
			if(l[l[x].pos[i]].dep>=l[lca].dep) base[i]=l[x].w[i];
			else base[i]=0;
		} 
		for(int i=D-1;i>=0;i--){
			if(l[l[y].pos[i]].dep>=l[lca].dep){
				int Y=l[y].w[i];
				for(int j=D-1;j>=0;j--){
					if((Y>>j)&1){
						if(base[j]) {
							Y^=base[j];
						}
						else{
							base[j]=Y;
							break;
						}
					}
				}
			}
		}
		int ans=0;
		for(int i=D-1;i>=0;i--) {
			ans=max(ans,ans^base[i]);
		}
		cout<<ans<<"\n";
	}
} 

P4301 [CQOI2013] 新Nim游戏

  • 题意:

给你 n 堆火柴玩 Nim 游戏,你先手,该如何拿才能保证赢

  • 思路:

Nim 游戏其实就是看异或和,异或和不为 0 则先手赢,所以我们遍历一遍火柴堆,如果不能插入,说明会使异或和为 0 所以就要加上。

因为要求最小,所以要从大到小排下序。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int D=64;
ll w[D];
bool insert(ll x){
	for(int i=D-1;i>=0;i--){
		if((x>>i)&1){
			if(w[i]){
				x^=w[i];
			}
			else{
				w[i]=x;
				return true;
			}
		}
	}
	return false;
}
int n;
ll a[100004];
bool cmp(int a,int b){
	return a>b;
}
ll ans;
int main(){
	cin>>n;
	for(int i=1;i<=n;i++) cin>>a[i];
	sort(a+1,a+1+n,cmp);
	for(int i=1;i<=n;i++){
		if(insert(a[i])){
			continue;
		}
		else{
			ans+=a[i];
		}
	}
	cout<<ans;
}
posted @   yueyan_WZF  阅读(13)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示