AT_abc235_e [ABC235E] MST + 1 题解
这题可以使用 \(\text{Kruskal}\) 来求解。
如果每次询问都重新求一次最小生成树的话,数据范围太大很明显会超时。所以我们可以考虑离线操作。
首先我们先把 \(m+q\) 条边按权值小到大排序。记得把询问的那 \(q\) 条标记一下,方便后面判断。
接下来遍历每条边:
-
并查集判断如果这条边的两个结点已经连通,直接跳过。
-
否则进行以下操作:
-
如果是原来的 \(m\) 条边,直接并查集合并两条边。
-
如果是询问的 \(q\) 条边,不进行合并操作,遍历到了当前边说明当前边将会是新最小生成树的边,记录下来。
-
最后把询问记录下来结果的输出即可。
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
const int N=2e5+100;
int n,m,u,v,d,q,fa[N],cnt;
struct node
{
int x,y,val,id;
}a[N*2];
bool r[N];
bool cmp(node a1,node a2)
{
return a1.val<a2.val;
}
int find1(int dx)//并查集查询祖先
{
return dx==fa[dx]?dx:fa[dx]=find1(fa[dx]);
}
void merge1(int dx,int dy)//并查集合并
{
int gx=find1(dx),gy=find1(dy);
if(gx!=gy) fa[gx]=gy;
}
int main()
{
scanf("%d%d%d",&n,&m,&q);
for(int i=1;i<=n;i++) fa[i]=i;
for(int i=1;i<=m;i++) scanf("%d%d%d",&a[i].x,&a[i].y,&a[i].val);
for(int i=1;i<=q;i++)
{
scanf("%d%d%d",&a[m+i].x,&a[m+i].y,&a[m+i].val);
a[m+i].id=i;//记录当前边的查询编号
}
m+=q;
sort(a+1,a+m+1,cmp);//按边权值从小到大排序
for(int i=1;i<=m;i++)//Kruskal
{
if(find1(a[i].x)!=find1(a[i].y))//两个结点不连通
{
//printf("%d %d %d %d\n",a[i].x,a[i].y,a[i].val,a[i].id);
if(a[i].id) r[a[i].id]=true;//有查询编号标记,是查询边,有贡献,标记
else merge1(a[i].x,a[i].y),cnt++;//否则是原来的边,合并
}
if(cnt==n-1) break;//最小生成树生成完成
}
for(int i=1;i<=q;i++) r[i]?printf("Yes\n"):printf("No\n");//输出查询的记录
return 0;
}

浙公网安备 33010602011771号