Codeforces Round #446 (Div. 1)C题

核心思路

这道题考察了一些最小生成树与边有关的的性质:对于一个图的所有 MST\mathrm{MST},每种边权的边的数量都相等。

那么我们可以分别考虑每一种边权的边,将其还原至加入这条边以前的状态然后再检验是否有环。

考虑如何还原到此前的状态,可持久化并查集,直接将加入该种边时两端点的联通块记录下来,在询问这种边的时候直接更改即可。

完整代码

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
72
73
74
75
76
77
78
79
80
81
82
83
84
85
#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>

using namespace std;

#define REP(i,e,s) for(register int i=e; i<=s; i++)
#define DREP(i,e,s) for(register int i=e; i>=s; i--)
#define ll long long
#define DE(...) fprintf(stderr,__VA_ARGS__)
#define DEBUG(a) DE("DEBUG: %d\n",a)
#define file(a) freopen(a".in","r",stdin);freopen(a".out","w",stdout)
int read() {
int x=0,f=1,ch=getchar();
while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}

const int MAXN=500000+10;

struct edge {
int u,v,w,id;
} a[MAXN],p[MAXN];

bool cmp1(edge a,edge b) {
return a.w<b.w;
}

bool cmp2(edge a,edge b) {
return a.id<b.id;
}

int fa[MAXN];

int find(int x) {
if(fa[x]==x) return x;
return fa[x]=find(fa[x]);
}

int link(int x,int y) {
fa[find(x)]=find(y);
}

int main() {
int n=read(),m=read();
REP(i,1,m) a[i]=(edge){read(),read(),read(),i};

sort(a+1,a+1+m,cmp1);

REP(i,1,n) fa[i]=i;

for(int i=1,j=1; i<=m; i=j=j+1) {
while(j<m&&a[j].w==a[j+1].w) j++;
REP(k,i,j) a[k].u=find(a[k].u),a[k].v=find(a[k].v);
REP(k,i,j) {
if(find(a[k].u)==find(a[k].v)) continue;
link(a[k].u,a[k].v);
}
}

sort(a+1,a+1+m,cmp2);

int q=read();

REP(t,1,q) {
bool flag=1;
int w=read();
REP(i,1,w) p[i]=a[read()];
sort(p+1,p+1+w,cmp1);
for(int i=1,j=1; i<=w; i=j=j+1) {
while(j<w&&p[j].w==p[j+1].w) j++;
REP(k,i,j) fa[p[k].u]=p[k].u,fa[p[k].v]=p[k].v;
REP(k,i,j) {
if(find(p[k].u)==find(p[k].v)) flag=0;
link(p[k].u,p[k].v);
}
}
if(flag) puts("YES");
else puts("NO");
}
return 0;
}

评论