ABC235E-MST+1题解

ABC235E MST+1 题解

题目大意

给你一个图 \(G\) 含有 \(n\) 个顶点和 \(m\) 条边,给出 \(Q\) 次询问,询问所多输入的边加上原图 \(G\) 的最小生成树是否是存在 \(e_i\),存在输出 Yes 不存在输出 No

思路

首先我们可以想到用最简单的方法,在每次询问时都做一次 Kruskal 算法,如果这个边被取到了,那么输出 Yes,反之亦然。时间复杂度为 \(\mathcal{O}(Q \times n \log n)\),太慢。

因为每个询问是相互独立的,而且询问的边 \(e_i\) 对于 并查集 的不会产生影响,所以可以想到只用一次 Kruskal 算法,把所有的边都涵盖上。

在遍历时每条边的处理方法:

  • 如果是询问的 \(e_i\) 的边

    • 如果 \(e_i\) 的所连的两个顶点 \(u,v\) 没有连通,则标记为 Yes

    • 如果连通,则标记为 No

  • 如果是询问的 \(e_i\) 的边

    • 如果 \(e_i\) 的所连的两个顶点 \(u,v\) 没有连通,则标记为 Yes

    • 如果连通,则标记为 No

  • 如果是 \(G\) 的边        

    • 将两个顶点在并查集内合并

时间复杂度 \(\mathcal{O}((n+Q) \log (n + Q))\)

代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<vector>

using namespace std;

const int maxn = 200005;

struct Edge{int u,v,w,Is,num;};
int fa[maxn];
Edge e[maxn*2];
bool ans[maxn];
int n,m,q;
vector<Edge> qe;

bool operator ==(const Edge &a,const Edge &b){return (a.u == b.u && a.v == b.v && a.w == b.w);}

int found(int x){
return fa[x] == x ? x : fa[x] = found(fa[x]);
}


bool cmp(Edge x,Edge y){
return x.w < y.w;
}

inline void Init_Onion(){
for(int x=1;x<=n;x++) fa[x] = x;
memset(ans,0,sizeof(ans));
}


int main()
{
scanf("%d %d %d",&n,&m,&q);
for(int i=1;i<=m;i++){
scanf("%d %d %d",&e[i].u,&e[i].v,&e[i].w);
e[i].Is = 0;
}

for(int i=1;i<=q;i++){
scanf("%d %d %d",&e[m+i].u,&e[m+i].v,&e[m+i].w);
e[m+i].Is = 1;e[m+i].num = i;
}

sort(e+1,e+m+q+1,cmp);
Init_Onion();

int k = 0;
for(int i=1;i<=m+q;i++){
if(e[i].Is == 1){
if(found(e[i].v) != found(e[i].u))
ans[e[i].num] = 1;
continue;
}

int u = e[i].u,v = e[i].v;
int f = found(u),t = found(v);
fa[t] = f;
}

for(int i=1;i<=q;i++){
if(ans[i] == 1)
printf("Yes\n");
else
printf("No\n");#191B1C
}
return 0;
}

本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!