NEFU大一ACM集训(一)
并查集:
一:并查集的定义
二:并查集的过程以及基本操作
三:并查集的应用:
- 对于定义的使用——判断图是否是树
- 最小生成树问题
四:两种特殊的并查集: - 边带权并查集
- 种类并查集
四:相应的题目
并查集的定义: 并查集是一种树型的数据结构,用于动态处理一些不相交集合(Disjoint Sets)的合并及查询问题。
并查集的过程以及基本操作:
- 过程: 运用并查集的过程就是先将每一个元素看成一个独立的集合,这个时候每一个集合的标识,再将不同的元素(集合)两两合并到一个集合当中,这就是并查集的合并,在合并的过程中这两个元素就连接在一起了,其中一个元素变成儿子,一个元素变成了父亲,以此类推那么一个集合中就会只有一个总父亲,总父亲的父亲是它自己,其他相对都是它儿子或者儿子的n次方,如果我们要找一个元素在不在一个集合当中,只要找到它的总父亲就行。
2.基本操作:
创造并查集:我们使用以一个数组的形式去模拟链表,假设有一个pre[i]的数组,i表示着第几个元素,而pre[i]的值表示的是这个元素的父亲,一开始只要把他们的的父亲设置成他们自己即可。
查找元素是否在相同的集合: 通过一个循环不断地去向上找,找到一个pre[i]=i的元素即可。
普通写法:
int find_(int x)
{
int r=x;
while(r!=pre[r])r=pre[r];
return r;
}
压缩写法:即把同一个总父亲的所有元素直接与总父亲相连,这样在查找的时候就可以缩短树的长度。在这里我提供两种写法:一种是循环写法,一种是递归写法
循环写法:
int find_(int x)
{
int r=x;
while(r!=pre[r])r=pre[r];
int tmp,i=x;
while(i!=r)
{
tmp=pre[i];
pre[i]=r;
i=tmp;
}
return r;
}
递归写法:
int find_(int x)
{
if(pre[x]==x)return pre[x];
int fn=find_(pre[x]);
return pre[x]=fn;
}
合并集合:两个不同的总父亲所代表的集合,只要把一个总父亲接到另一个总父亲身上,将其自身变为儿子,即可。
void merge_(int x,int y)
{
int tmp1=find_(x);
int tmp2=find_(y);
if(tmp1!=tmp2)pre[tmp1]=tmp2;
}
并查集的应用:
一:判断图是否是树,既然每个树的节点都是互通的,那么只要找一个节点的总父亲,就是这个树的总父亲,如果有节点的父亲是它本身且不是总父亲,那么这个图必然不是树。
二:最小生成树:在一给定的无向图G = (V, E) 中,(u, v) 代表连接顶点 u 与顶点 v 的边(即),而 w(u, v) 代表此边的权重,若存在 T 为 E 的子集(即)且为无循环图,使得的 w(T) 最小,则此 T 为 G 的最小生成树并且最小生成树是最小权重生成树的简称。
模板代码:这里的代码是基于Kruskal(克鲁斯卡尔)算法:求最小生成树的话给它的权值从小到大排序,然后将两个点按照权值的大小的顺序连起来,如果两个点已经连接则不需要连接,当树的节点达到图的节点减一时则不再连接,得到的树就是最小生成树,它本质还是用贪心得到的。
#include <bits/stdc++.h>
using namespace std;
const int N=1e3;
int pre[N];
struct node
{
int u,v,c;
}vis[N];//用结构体包含起点终点和权值
bool cmp(struct node x,struct node y)//权值排序
{
return x.c<y.c;
}
int find_(int x)//找父亲
{
int r=x;
while(r!=pre[r])r=pre[r];
int tmp,i=x;
while(i!=r)
{
tmp=pre[i];
pre[i]=r;
i=tmp;
}
return r;
}
void merge_(int x,int y)//合并
{
int tmp1=find_(x);
int tmp2=find_(y);
if(tmp1!=tmp2)pre[tmp1]=tmp2;
}
int main()
{
int n,m;//n是点的个数,m是边的条数。
while(cin>>n>>m)
{
int num;
num=0;
memset(pre,0,sizeof(pre));
for(int i=1;i<=n;i++)pre[i]=i;
for(int i=1;i<=m;i++)
{
cin>>vis[i].u>>vis[i].v>>vis[i].c>>vis[i].j;
}
sort(vis+1,vis+1+m,cmp);
for(int i=1;i<=m;i++)
{
int tmp2=find_(vis[i].u);
int tmp3=find_(vis[i].v);
if(tmp2!=tmp3)//判断两点是否要相连
{
merge_(vis[i].u,vis[i].v);
num++;
}
if(num==n-1)break;//这个if不能放到循环的开始,不然会有只走一次且合格的数据会被判定不正确
}
}
return 0;
}
两种特殊的并查集:
- 边带权并查集:这里的权值是指的是每个点到达其所在的总父亲节点的长度,那么我们一共需要三个数组,一个数组表示每个节点的父亲,一个数组表示每个节点到达其所在的总父亲的长度,一个数组表示每个节点所在的集合的大小,因为在两个总父亲A,B合并的时候会使成为儿子A的一方的顶点到达新的总父亲B一方的长度变为之前B的集合的大小,而新的父亲的集合大小变为A的集合大小加B的集合大小,有一个特点是每次查找都要从新更新一下,因为合并完以后它其实是并没有更新每个点到新的总父亲的距离的,只更新的以前的总父亲到新的总父亲,还有目前所在集合的大小,所以在找点的时候仍然要在查找一下,更新每个节点到新总父亲的距离。
题目:
Noip-P1196银河英雄传说
Noip-P2342搭积木
(题目的答案与思路放到后面Noip的题目中) - 种类并查集:普通的并查集只能表示一种的关系的两个分支,就像是一群人它是好人或者不是好人,而不能表示一群人中里面的有群好人,一群坏人,一群既不是好人也不是坏人(
这个时候最可爱的的你可能会想,人不是好人不就是坏人吗)。这个时候我们就要拓展并查集的域即并查集的大小,并根据分段来分不同的种类,首先根据不同域之间的关系判断元素在哪个域,处理不同域之间的关系直接处理就行,处理相同域的关系时,要同时处理所有的域中的要处理的元素。
题目:
P2024-食物链
P1892-团伙
(题目的答案与思路放到后面Noip的题目中)
通过这几个题目更加强调了,并查集是对于点和点之间边的意义的强调而不是对点的强调。
相应的题目:
1.Noip-P3366
思路:模板题,打完跑路,不熟悉可多手打几次。
#include <bits/stdc++.h>
using namespace std;
const int N=2e5+5;
int pre[N];
int m,n;
struct node
{
int x,y,z;
}vis[N];
bool cmp(struct node x,struct node y)
{
return x.z<y.z;
}
int find_(int x)
{
int r=x;
while(r!=pre[r])r=pre[r];
int tmp,i=x;
while(i!=r)
{
tmp=pre[i];
pre[i]=r;
i=tmp;
}
return r;
}
void merge_(int x,int y)
{
int tmp1=find_(x);
int tmp2=find_(y);
if(tmp1!=tmp2)pre[tmp1]=tmp2;
}
int main()
{
ios::sync_with_stdio(false);
cin>>n>>m;
for(int i=1;i<=m;i++)pre[i]=i;
for(int i=1;i<=m;i++)
cin>>vis[i].x>>vis[i].y>>vis[i].z;
sort(vis+1,vis+1+m,cmp);
int sum,num;
sum=num=0;
for(int i=1;i<=m;i++)
{
if(num==n-1)break;
int x,y,z,tmp1,tmp2;
x=vis[i].x;y=vis[i].y;z=vis[i].z;
tmp1=find_(x);tmp2=find_(y);
if(tmp1!=tmp2)
{
merge_(x,y);
sum+=z;
num++;
}
}
cout<<sum<<endl;
return 0;
}
2.Noip-P1536
思路:把点都合并完,看看有谁的父亲还是自己,数完以后减一,减去一个连接着很多儿子的总父亲就行了。
#include<bits/stdc++.h>
using namespace std;
const int N=1e3+5;
int pre[N];
int find_(int x)
{
int r=x;
while(r!=pre[r])r=pre[r];
int tmp,i=x;
while(i!=r)
{
tmp=pre[i];
pre[i]=r;
i=tmp;
}
return r;
}
void merge_(int x,int y)
{
int tmp1=find_(x);
int tmp2=find_(y);
if(tmp1!=tmp2)pre[tmp1]=tmp2;
}
int main()
{
ios::sync_with_stdio(false);
int n,m,x,y,sum;
while(cin>>n>>m)
{
if(n==0)break;
sum=0;
for(int i=1; i<=n; i++)pre[i]=i;
for(int i=1; i<=m; i++)
{
cin>>x>>y;
merge_(x,y);
}
for(int i=1; i<=n; i++)
{
if(pre[i]==i)sum++;
}
sum--;
cout<<sum<<endl;
}
return 0;
}
3.Noip-P2820
思路:也是一道最小生成树的题,它要减去的边最大,那么我先求减去的边最小之和再拿总边减这个和就行了,也是一道模板题。
#include<bits/stdc++.h>
using namespace std;
const int N=1e3+5;
int pre[N];
struct node
{
int i,j,m;
}vis[N];
bool cmp(struct node x, struct node y)
{
return x.m<y.m;
}
int find_(int x)
{
int r=x;
while(r!=pre[r])r=pre[r];
int tmp,i=x;
while(i!=r)
{
tmp=pre[i];
pre[i]=r;
i=tmp;
}
return r;
}
void merge_(int x,int y)
{
int tmp1=find_(x);
int tmp2=find_(y);
if(tmp1!=tmp2)pre[tmp1]=tmp2;
}
int main()
{
int n,k,tmp,sum,num,res;
tmp=sum=num=0;
cin>>n>>k;
for(int i=1;i<=n;i++)pre[i]=i;
for(int i=1;i<=k;i++)
{
cin>>vis[i].i>>vis[i].j>>vis[i].m;
sum+=vis[i].m;
}
sort(vis+1,vis+1+k,cmp);
for(int i=1;i<=k;i++)
{
if(num==n-1)break;
int tmp1=find_(vis[i].i);
int tmp2=find_(vis[i].j);
if(tmp1!=tmp2)
{
merge_(vis[i].i,vis[i].j);
tmp+=vis[i].m;
num++;
}
}
res=sum-tmp;
cout<<res<<endl;
return 0;
}
4.Noip-P1547
思路:最小生成树然后找到最大的边长就可以了。
#include<bits/stdc++.h>
using namespace std;
const int N=1e4+5;
int pre[N];
struct node
{
int a,b,l;
}vis[N];
bool cmp(struct node x,struct node y)
{
return x.l<y.l;
}
int find_(int x)
{
int r=x;
while(r!=pre[r])r=pre[r];
int tmp,i=x;
while(i!=r)
{
tmp=pre[i];
pre[i]=r;
i=tmp;
}
return r;
}
void merge_(int x,int y)
{
int tmp1=find_(x);
int tmp2=find_(y);
if(tmp1!=tmp2)pre[tmp1]=tmp2;
}
int main()
{
int n,m,sum,num;
sum=num=0;
cin>>n>>m;
for(int i=1;i<=n;i++)pre[i]=i;
for(int i=1;i<=m;i++)
cin>>vis[i].a>>vis[i].b>>vis[i].l;
sort(vis+1,vis+1+m,cmp);
for(int i=1;i<=m;i++)
{
if(num==n-1)break;
int tmp1=find_(vis[i].a);
int tmp2=find_(vis[i].b);
if(tmp1!=tmp2)
{
merge_(vis[i].a,vis[i].b);
sum=max(sum,vis[i].l);
num++;
}
}
cout<<sum<<endl;
return 0;
}
5.Noip-P1195
思路:这里的变化是连成K个棉花糖,之前的最小生成树是连出一个树即n个点连出n-1个边,而连出k个树就是n个点连接n-k个边。
#include<bits/stdc++.h>
using namespace std;
const int N=1e4+5;
int pre[N];
struct node
{
int a,b,l;
} vis[N];
bool cmp(struct node x,struct node y)
{
return x.l<y.l;
}
int find_(int x)
{
int r=x;
while(r!=pre[r])r=pre[r];
int tmp,i=x;
while(i!=r)
{
tmp=pre[i];
pre[i]=r;
i=tmp;
}
return r;
}
void merge_(int x,int y)
{
int tmp1=find_(x);
int tmp2=find_(y);
if(tmp1!=tmp2)pre[tmp1]=tmp2;
}
int main()
{
int n,m,k,sum,num,flag=0;
cin>>n>>m>>k;
sum=num=0;
for(int i=1; i<=n; i++)pre[i]=i;
for(int i=1; i<=m; i++)
cin>>vis[i].a>>vis[i].b>>vis[i].l;
sort(vis+1,vis+1+m,cmp);
for(int i=1; i<=m; i++)
{
if(num==n-k){flag=1;break;}
int tmp1=find_(vis[i].a);
int tmp2=find_(vis[i].b);
if(tmp1!=tmp2)
{
merge_(vis[i].a,vis[i].b);
sum+=vis[i].l;
num++;
}
}
if(flag)cout<<sum<<endl;
else cout<<"No Answer"<<endl;
return 0;
}
6.Noip-P1196银河英雄传说
思路:开三个数组,每次在找父亲的时候记录每个点回溯到总父亲的长度,路径压缩的时候推荐用递归算法,不仅仅是他只需要写4行,而是用while的路径压缩的时候只跑了一次,并没有对每个节点进行回溯,合并的时候注意顶点到合并后顶点的距离,以及合并后两个顶点所代表的的集合长度的大小。
#include<bits/stdc++.h>
using namespace std;
const int N=3e4+5;
int pre[N];
int dis[N],siz[N];
int find_(int x)
{
int contain,r=x;
while(r!=pre[r])r=pre[r];
int tmp,i=x;
while(i!=r)
{
tmp=pre[i];
dis[i]+=dis[tmp];
pre[i]=r;
i=tmp;
}
return r;
}
void merge_(int x,int y)
{
int tmp1=find_(x);
int tmp2=find_(y);
if(tmp1!=tmp2)
{
pre[tmp1]=tmp2;
dis[tmp1]+=siz[tmp2];
siz[tmp1]+=siz[tmp2];
siz[tmp2]=siz[tmp1];
}
}
int main()
{
int t;
cin>>t;
for(int i=1; i<=N; i++)
{
pre[i]=i;
dis[i]=0;
siz[i]=1;
}
while(t--)
{
char judge;
int i,j;
cin>>judge>>i>>j;
if(judge=='M')merge_(i,j);
else
{
int tmp1=find_(i);
int tmp2=find_(j);
if(tmp1!=tmp2)cout<<"-1"<<endl;
else
{
int sum=0;
sum=abs(dis[i]-dis[j])-1;
cout<<sum<<endl;
}
}
}
return 0;
}
7.Noip-P2342搭积木
思路:还是带权并查集,但是要注意一个特点就是即使查找一个的时候也要重新更新一下,它到总父亲的长度,不然永远无法AC。
#include<bits/stdc++.h>
using namespace std;
const int N=1e+5;
int pre[N],dis[N],siz[N],vis[N];
int find_(int x)
{
if(x==pre[x])return x;
int tmp=find_(pre[x]);
dis[x]+=dis[pre[x]];
return pre[x]=tmp;
}
int merge_(int x,int y)
{
int tmp1=find_(x);
int tmp2=find_(y);
if(tmp1!=tmp2)
{
pre[tmp1]=tmp2;
dis[tmp1]=siz[tmp2];
siz[tmp2]+=siz[tmp1];
}
}
int main()
{
ios::sync_with_stdio(false);
int p;
cin>>p;
for(int i=1;i<=N;i++){pre[i]=i;dis[i]=0;siz[i]=1;}
while(p--)
{
char judge;
int x,y,z;
cin>>judge;
if(judge=='M'){cin>>x>>y;merge_(x,y);}
else
{
cin>>z;
find_(z);
cout<<dis[z]<<endl;
}
}
return 0;
}
8.P2024-食物链
思路:先通过谁吃谁的关系判断它在哪个域中,如果它被吃或者吃别人而在相同的域必然说谎,或者它时吃别人的却被吃或它被吃却吃别人则它必然说谎,一定要判断到第二种情况,而第二种情况的A,B,C的三个域中只要是元素在任何一个域都会产生不同的效果,所以第一种情况的反面不是第二种情况并且第二种情况要复杂得多,判断出是相同域的操作是同时操作三个域里面的两个元素,这两个题我过几天写两个专门的题解放着。
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+5;
int pre[N*3];
int find_(int x)
{
int r=x;
while(r!=pre[r])r=pre[r];
return r;
}
int main()
{
int n,k,j,x,y,res=0;
ios::sync_with_stdio(false);
cin>>n>>k;
for(int i=1;i<=n*3;i++)pre[i]=i;
for(int i=1;i<=k;i++)
{
cin>>j>>x>>y;
if(x>n||y>n){res++;continue;}
else
{
if(j==1)
{
if((find_(x+n)==find_(y))||(find_(x)==find_(y+n))){res++;}
else
{
pre[find_(x)]=find_(y);
pre[find_(x+n)]=find_(y+n);
pre[find_(x+n+n)]=find_(y+n+n);
}
}
else
{
if((find_(x)==find_(y))||(find_(x)==find_(y+n))){res++;}
else
{
pre[find_(x+n)]=find_(y);
pre[find_(x+n+n)]=find_(y+n);
pre[find_(x)]=find_(y+n+n);
}
}
}
}
cout<<res<<endl;
return 0;
}
以下题目来源于东北林业大学OJ网:http://acm.nefu.edu.cn/problem.php
有兴趣可以直接搜题目名称做题。
1.畅通工程并查集版
思路:模板题,打完跑路。
#include <bits/stdc++.h>
using namespace std;
const int N=1e3+5;
int pre[N];
int find_(int x)
{
int r=x;
while(r!=pre[r])r=pre[r];
int tmp,i=x;
while(i!=r)
{
tmp=pre[i];
pre[i]=r;
i=tmp;
}
return r;
}
void merge_(int x,int y)
{
int tmp1=find_(x);
int tmp2=find_(y);
if(tmp1!=tmp2)pre[tmp1]=tmp2;
}
int main()
{
ios::sync_with_stdio(false);
int n,m,u,v,sum;
while(cin>>n>>m)
{
if(n==0)break;
sum=0;
for(int i=1;i<=n;i++)pre[i]=i;
for(int i=1;i<=m;i++)
{
cin>>u>>v;
merge_(u,v);
}
for(int i=1;i<=n;i++)
if(pre[i]==i)sum++;
sum--;
cout<<sum<<endl;
}
return 0;
}
2.小希的迷宫
思路:因为它一定是树,所以m=n-1不然就不是树,然后用一个桶排序记录它是否存在就行了,当然用set更快。
#include <bits/stdc++.h>
using namespace std;
const int N=1e5+5;
int pre[N];
bool vis[N];
int main()
{
int x,y,num,sum,maxn,flag=1;
while(flag)
{
num=sum=maxn=0;
memset(vis,0,sizeof(vis));
memset(pre,0,sizeof(pre));
while(scanf("%d %d",&x,&y)&&x&&y)
{
if(x==-1&&y==-1){flag=0;break;}
vis[x]=1;vis[y]=1;
maxn=max(x,maxn);
maxn=max(y,maxn);
sum++;
}
if(!flag)break;
for(int i=1;i<=maxn;i++)if(vis[i]==1)num++;
if(sum==num-1)printf("Yes\n");
else printf("No\n");
}
return 0;
}
3.湖南修路
思路:注意一点最后面判断是1的时候要插进去。
#include <bits/stdc++.h>
using namespace std;
const int N=1e3;
int pre[N];
struct node
{
int u,v,c,j;
}vis[N];
bool cmp(struct node x,struct node y)
{
return x.c<y.c;
}
int find_(int x)
{
int r=x;
while(r!=pre[r])r=pre[r];
int tmp,i=x;
while(i!=r)
{
tmp=pre[i];
pre[i]=r;
i=tmp;
}
return r;
}
void merge_(int x,int y)
{
int tmp1=find_(x);
int tmp2=find_(y);
if(tmp1!=tmp2)pre[tmp1]=tmp2;
}
int main()
{
int n;
while(cin>>n)
{
int tmp,sum,res,num;
sum=res=num=0;
memset(pre,0,sizeof(pre));
for(int i=1;i<=n;i++)pre[i]=i;
tmp=n*(n-1)/2;
for(int i=1;i<=tmp;i++)
{
cin>>vis[i].u>>vis[i].v>>vis[i].c>>vis[i].j;
sum+=vis[i].c;
if(vis[i].j)merge_(vis[i].u,vis[i].v);
}
sort(vis+1,vis+1+tmp,cmp);
for(int i=1;i<=tmp;i++)
{
if(num==n-1)break;
int tmp2=find_(vis[i].u);
int tmp3=find_(vis[i].v);
if(tmp2!=tmp3&&!vis[i].j)
{
merge_(vis[i].u,vis[i].v);
res+=vis[i].c;
num++;
}
}
cout<<res<<endl;
}
return 0;
}
4.最小树1
思路:把结构体里记录长度的变量变成float型,又整型变成浮点型的时候cmp并没有失效。
#include <bits/stdc++.h>
using namespace std;
const int N=1e3;
int pre[N];
struct node
{
int u,v;
float c;
}vis[N];
bool cmp(struct node x,struct node y)
{
return x.c<y.c;
}
int find_(int x)
{
int r=x;
while(r!=pre[r])r=pre[r];
int tmp,i=x;
while(i!=r)
{
tmp=pre[i];
pre[i]=r;
i=tmp;
}
return r;
}
void merge_(int x,int y)
{
int tmp1=find_(x);
int tmp2=find_(y);
if(tmp1!=tmp2)pre[tmp1]=tmp2;
}
int main()
{
int n;
while(scanf("%d",&n)&&n)
{
int tmp,num;
float sum,res;
sum=res=num=0;
memset(pre,0,sizeof(pre));
for(int i=1;i<=n;i++)pre[i]=i;
tmp=n*(n-1)/2;
for(int i=1;i<=tmp;i++)
{
scanf("%d %d %f",&vis[i].u,&vis[i].v,&vis[i].c);
sum+=vis[i].c;
}
sort(vis+1,vis+1+tmp,cmp);
for(int i=1;i<=tmp;i++)
{
if(num==n-1)break;
int tmp2=find_(vis[i].u);
int tmp3=find_(vis[i].v);
if(tmp2!=tmp3)
{
merge_(vis[i].u,vis[i].v);
res+=vis[i].c;
num++;
}
}
printf("%.2f\n",res);
}
return 0;
}
5.修路工程
思路:判断能不能生成最小生成树,不能就问好,能就输出。
#include <bits/stdc++.h>
using namespace std;
const int N=1e3;
int pre[N];
struct node
{
int u,v,c;
}vis[N];
bool cmp(struct node x,struct node y)
{
return x.c<y.c;
}
int find_(int x)
{
int r=x;
while(r!=pre[r])r=pre[r];
int tmp,i=x;
while(i!=r)
{
tmp=pre[i];
pre[i]=r;
i=tmp;
}
return r;
}
void merge_(int x,int y)
{
int tmp1=find_(x);
int tmp2=find_(y);
if(tmp1!=tmp2)pre[tmp1]=tmp2;
}
int main()
{
int n,m;
while(cin>>n>>m)
{
if(n==0)break;
int num,sum,flag;
num=sum=flag=0;
memset(pre,0,sizeof(pre));
for(int i=1;i<=m;i++)pre[i]=i;
for(int i=1;i<=n;i++)
cin>>vis[i].u>>vis[i].v>>vis[i].c;
sort(vis+1,vis+1+n,cmp);
for(int i=1;i<=n;i++)
{
int tmp2=find_(vis[i].u);
int tmp3=find_(vis[i].v);
if(tmp2!=tmp3)
{
merge_(vis[i].u,vis[i].v);
sum+=vis[i].c;
num++;
}
if(num==m-1){flag=1;break;}
}
if(flag)cout<<sum<<endl;
else cout<<"?"<<endl;
}
return 0;
}
6.一道图论一其实是loj136
思路:正常的生成最小生成树,由于最小生成树的每个边的权值都是最小的记录下来就完事了,这个题还是问了jsc大佬才整出来的,再次致谢,一开始还想生成树+深搜,有点憨憨。
#include <bits/stdc++.h>
using namespace std;
const int N=1e5+5;
int pre[N];
struct node
{
int u,v,w;
}vis[N],eqr[N];
bool cmp(struct node x,struct node y)
{
return x.w<y.w;
}
int find_(int x)
{
int r=x;
while(r!=pre[r])r=pre[r];
int tmp,i=x;
while(i!=r)
{
tmp=pre[i];
pre[i]=r;
i=tmp;
}
return r;
}
void merge_(int x,int y)
{
int tmp1=find_(x);
int tmp2=find_(y);
if(tmp1!=tmp2)pre[tmp1]=tmp2;
}
int main()
{
int n,m,k,maxn;
maxn=0;
cin>>n>>m>>k;
for(int i=1;i<=n;i++)pre[i]=i;
for(int i=1;i<=m;i++)
cin>>vis[i].u>>vis[i].v>>vis[i].w;
sort(vis+1,vis+1+m,cmp);
for(int i=1;i<=k;i++)
{
cin>>eqr[i].u>>eqr[i].v;
eqr[i].w=-1;
}
for(int i=1;i<=m;i++)
{
merge_(vis[i].u,vis[i].v);
maxn=vis[i].w;
for(int j=1;j<=k;j++)
{
if(eqr[j].w==-1)
{
int tmp3=find_(eqr[j].u);
int tmp4=find_(eqr[j].v);
if(tmp3==tmp4){eqr[j].w=maxn;}
}
}
}
for(int i=1;i<=k;i++)cout<<eqr[i].w<<endl;
return 0;
}
7.藤原千花的星星图
思路:注意long long型,和数据量很大要开O2优化,不然会超时。
#pragma GCC optimize(2)
#include<bits/stdc++.h>
using namespace std;
const int N=1e6+5;
int pre[N];
struct node
{
int u,v;
long long c;
}vis[N];
bool cmp(struct node x,struct node y)
{
return x.c<y.c;
}
int find_(int x)
{
int r=x;
while(r!=pre[r])r=pre[r];
int tmp,i=x;
while(i!=r)
{
tmp=pre[i];
pre[i]=r;
i=tmp;
}
return r;
}
void merge_(int x,int y)
{
int tmp1=find_(x);
int tmp2=find_(y);
if(tmp1!=tmp2)pre[tmp1]=tmp2;
}
int main()
{
int n,m,num,flag;
long long sum;
while(scanf("%d %d",&n,&m)!=EOF)
{
sum=0;num=flag=0;
for(int i=1;i<=n;i++)pre[i]=i;
for(int i=1;i<=m;i++)
scanf("%d %d %lld",&vis[i].u,&vis[i].v,&vis[i].c);
sort(vis+1,vis+1+m,cmp);
for(int i=1;i<=m;i++)
{
int tmp1=find_(vis[i].u);
int tmp2=find_(vis[i].v);
if(tmp1!=tmp2)
{
merge_(vis[i].u,vis[i].v);
sum+=vis[i].c;
num++;
}
if(num==n-1){flag=1;break;}
}
if(flag)printf("%lld\n",sum);
else printf("-1\n");
}
return 0;
}