并查集

void init(){
	for(int i=1;i<=n;i++) p[i]=i;
}
int find(int x){
	if(p[x]!=x) p[x]=find(p[x]);
	return p[x];
}
void merge(int a,int b){
	p[a]=b;
	sz[b]+=sz[a];//维护并查集大小 
}

维护到根节点的距离

//维护到根节点距离
int find(int x){
	if(p[x]!=x){
		int t=find(p[x]);//更新p[x]到根节点的距离 
		d[x]+=d[p[x]];//维护到根节点的距离 
		p[x]=t;//更新父节点 
	}
	return p[x];
}
void merge(int a,int b){
	p[a]=b;
	d[a]+=sz[b];//维护到根节点的距离
	sz[b]+=sz[a];//维护并查集大小 
}

食物链

食物链 - AcWing

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+5;
int p[N],d[N];
int n,k,res; 
//d[x]-d[y]%3==0表示x和y同类
//d[x]-d[y]%3==1表示x吃y
int find(int x){
	if(p[x]!=x){
		int t=find(p[x]);//更新p[x]到根节点的距离 
		d[x]+=d[p[x]];//维护到根节点的距离 
		p[x]=t;//更新父节点 
	}
	return p[x];
}
int main()
{
	cin>>n>>k;
	for(int i=1;i<=n;i++) p[i]=i;
	while(k--){
		int t,x,y;cin>>t>>x>>y;
		if(x>n||y>n||t==2&&x==y){
			res++;
			continue; 
		}
		int px=find(x),py=find(y);
		//px==py表示x和y之间已经有联系(在同一集合中) 
		if(t==1){ 
			if(px==py&&(d[x]-d[y])%3!=0) res++;
			else if(px!=py){
				p[px]=py;//x和y所在集合合并 
				d[px]=d[y]-d[x];//保持d[x]-d[y] %3==0 
			} 
		}else{
			if(px==py&&(d[x]-d[y]-1)%3!=0) res++;
			else if(px!=py){
				p[px]=py;
				d[px]=d[y]-d[x]+1;//保持d[x]-d[y]-1 %3==0 
			} 	
		}
	}
	cout<<res;
} 

奇偶游戏

239. 奇偶游戏 - AcWing题库

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+5;
int p[N];
int d[N];
int n,m;
//前缀和思想
//s[l~r]中1的数量为偶数,说明s[r]-s[l-1]为偶数
//即s[l-1]和s[r]奇偶性相同
//类比 食物链题目 
//d[x]-d[y]%2==0  s[x] s[y]奇偶性相同
//d[x]-dy%2==1 s[x] s[y]奇偶性不同 
int find(int x){
	if(p[x]!=x){
		int t=find(p[x]);
		d[x]+=d[p[x]];
		p[x]=t;
	}
	return p[x];
}
int res;
int cnt;
map<int,int> mp;//离散化 
int get(int x){
	if(!mp.count(x)) mp[x]=++cnt;
	return mp[x];
}
int main(){
	cin>>n>>m;
	for(int i=1;i<N;i++) p[i]=i;
	for(int i=1;i<=m;i++){
		int x,y;string op;
		cin>>x>>y>>op;
		x=get(x-1),y=get(y);
		int px=find(x),py=find(y);
		if(op=="even"){
			if(px==py&&(d[y]-d[x])%2!=0){
				res=i-1;
				break;
			}
			else if(px!=py){
				p[px]=py;
				d[px]=d[y]-d[x];
			}
		}else{
			if(px==py&&(d[y]-d[x]-1)%2!=0){
				res=i-1;
				break;
			}
			else if(px!=py){
				p[px]=py;
				d[px]=d[y]-d[x]+1;
			}
		}
	}
	cout<<(res==0?m:res);
}

搭配购买 

1252. 搭配购买 - AcWing

        将同类物品合并,01背包枚举时仅考虑并查集根节点物品

#include<bits/stdc++.h>
using namespace std;
const int N=1e4+5,M=1e4+5;
int p[N];
int n,k,m;
int v[N],w[N];
int find(int x)
{
	if(p[x]!=x) p[x]=find(p[x]);
	return p[x];
}
int f[M];
int dp(){
	for(int i=1;i<=n;i++)
		if(p[i]==i){
			for(int j=m;j>=v[i];j--){
				f[j]=max(f[j],f[j-v[i]]+w[i]);
			}
		}
	return f[m];
}
int main()
{
	cin>>n>>k>>m;
	for(int i=0;i<=n;i++) p[i]=i;
	for(int i=1;i<=n;i++) cin>>v[i]>>w[i];
	while(k--)
	{
		int a,b;cin>>a>>b;
		int pa=find(a),pb=find(b);
		if(pa!=pb){
			p[pa]=pb;
			v[pb]+=v[pa];
			w[pb]+=w[pa];
		} 
	}
	cout<<dp();
	
}

程序自动分析

237. 程序自动分析 - AcWing题库

        若干个条件,包括两个未知数是否相等或不相等,判断是否矛盾

        先将要求相等的并到一起,然后判断要求不相等的里面是否有并在一起的

注意:本题需要离散化,使用map映射下标,用下标代替数即可

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> PII;
const int N=1e5+5;
int p[N<<1];
int find(int x){
	if(p[x]!=x) p[x]=find(p[x]);
	return p[x];
}
unordered_map<int,int> mp;
int cnt;
//离散化 
int get(int x){
	if(!mp.count(x)){
		mp[x]=++cnt;
	}
	return mp[x];
}
vector<PII> eq,neq;
int main(){
	int t;cin>>t;
	while(t--){
		int n;cin>>n;
		cnt=0;
		mp.clear();
		eq.clear();
		neq.clear();
		for(int z=1;z<=n;z++){
			int i,j,e;cin>>i>>j>>e;
			i=get(i),j=get(j);
			if(e) eq.push_back({i,j});
			else neq.push_back({i,j});
		}
		for(int i=1;i<=cnt;i++) p[i]=i;
		//先合并相等的 
		for(auto t:eq){
			int a=t.first,b=t.second;
			a=find(a),b=find(b);
			p[a]=b;
		}
		//检查不相等的是否在一块 
		bool f=true;
		for(auto t:neq){
			int a=t.first,b=t.second;
			a=find(a),b=find(b);
			if(a==b){
				cout<<"NO"<<endl;
				f=false;
				break;
			}
		}
		if(f){
			cout<<"YES"<<endl;
		}
	} 
}

银河英雄传说

238. 银河英雄传说 - AcWing

        维护到根节点的距离以及并查集的大小

注意:本题中询问i号和j号之间相隔多少战舰,需要判断i和j是否为同一艘,如果为同一艘则为0,否则为i和j到根节点距离的差值-1

#include<bits/stdc++.h>
using namespace std;
const int N=3e4+5;
int p[N];
int sz[N];
int d[N];
int find(int x){
	if(p[x]!=x){
		int t=find(p[x]);
		d[x]+=d[p[x]];
		p[x]=t; 
	}
	return p[x]; 
}
int main(){
	for(int i=1;i<N;i++) p[i]=i,sz[i]=1;
	int t;cin>>t;
	while(t--){
		char c;int a,b;
		cin>>c>>a>>b;
		int pa=find(a),pb=find(b);
		if(c=='M'){
			if(pa!=pb){
				p[pa]=pb;
				d[pa]+=sz[pb];
				sz[pb]+=sz[pa];
			} 	
		}else{
			if(pa!=pb) cout<<"-1"<<endl;
			else{
				if(a==b) cout<<0<<endl;
				else cout<<abs(d[a]-d[b])-1<<endl;
			}
		}	
	}
} 

网络分析

2069. 网络分析 - AcWing题库

   做法一:每次合并时,如果这两个点不在同一连通块,则构造一个新点,使这个新点成为合并后的根节点,同时从根节点向两个点连边。最后从新点中的根节点fa开始DFS,将fa的权值传递给子节点

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> PII;
const int N=2e4+5,M=N<<2;
int n,m;
int p[N];
int h[N],e[M],ne[M],idx;
void add(int a,int b){
	e[idx]=b;
	ne[idx]=h[a];
	h[a]=idx++;
}
int f[N];
int new_node;
int find(int x){
	if(p[x]!=x) p[x]=find(p[x]);
	return p[x];
}
void dfs(int u,int fa){
	f[u]+=f[fa];
	for(int i=h[u];~i;i=ne[i]){
		int j=e[i];
		dfs(j,u);
	}
}
int main(){
	cin>>n>>m;
	new_node=n+1;
	memset(h,-1,sizeof h);
	for(int i=1;i<=2*n;i++) p[i]=i;
	while(m--){
		int c,a,b;cin>>c>>a>>b;
		if(c==1){
			a=find(a),b=find(b);
			if(a!=b){
				p[a]=p[b]=new_node;
				add(new_node,a);
				add(new_node,b);
				new_node++;
			}
		}else{
			a=find(a);
			f[a]+=b;
		}
	}
	for(int i=n+1;i<new_node;i++){
		if(p[i]==i) dfs(i,0);
	}
	for(int i=1;i<=n;i++){
		cout<<f[i]<<" ";
	}
} 

  做法二:维护到根节点的权值,一个点的权值等于从该点到根节点的路径上的所有点的权值之和。合并两个根节点时,例如将a合并到b上,为了保证并查集中的点之前的权值不受影响,需要将a的权值减去b的权值抵消,再将a的父节点更新为b. 最后查询时,对于根节点则直接输出权值,非根节点的权值等于该点权值加上根节点的权值

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> PII;
const int N=1e4+5,M=N<<1;
int n,m;
int p[N];
int d[N];
int h[N],e[M],ne[M],idx;
void add(int a,int b){
	e[idx]=b;
	ne[idx]=h[a];
	h[a]=idx++;
}
int find(int x){//x是根节点 或 x的父节点是根节点 则不压缩 
	if(p[x]!=x&&p[p[x]]!=p[x]){
		int fa=find(p[x]);
		d[x]+=d[p[x]];
		p[x]=fa;
	}
	return p[x];
}
int main(){
	cin>>n>>m;
	memset(h,-1,sizeof h);
	for(int i=1;i<=n;i++) p[i]=i;
	while(m--){
		int c,a,b;cin>>c>>a>>b;
		if(c==1){
			a=find(a),b=find(b);
			if(a!=b){
				d[a]-=d[b];
				p[a]=b;
			}
		}else{
			a=find(a);
			d[a]+=b;
		}
	}
	for(int i=1;i<=n;i++){
		if(find(i)==i) cout<<d[i]<<" ";
		else cout<<d[i]+d[find(i)]<<" ";
	}
} 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Vic.GoodLuck

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值