2020牛客暑期多校训练营(第三场)G Operating on a Graph
题解:
对于每一个节点,首先用并查集维护每一个节点属于哪一个组,然后再并节点,这个节点并起来可以用链式前向星来模拟,因为链式前向星是把一个点的所有的边记录变成一条链,所以并掉的点的临近节点就可以用前向星直接连起来,然后这个点把他周围的点并掉之后,他所连的边就是一些新的边了。
但是要注意的是,可能对于一个点,和他最近的x个点都连向同一个点,所以这个点就会和操作的点连很多次,这样就会 \(tle\)
#include <bits/stdc++.h>
#define inf 0x3f3f3f3f
#define inf64 0x3f3f3f3f3f3f3f3f
#define debug(x) printf("debug:%s=%lld\n",#x,x);
//#define debug(x) cout << #x << ": " << x << endl
using namespace std;
const int maxn = 8e5+10;
typedef long long ll;
int to[maxn<<1],head[maxn],nxt[maxn<<1],ed[maxn],cnt;
void add(int u,int v) {
++cnt, to[cnt] = v, nxt[cnt] = head[u], head[u] = cnt;
if (!ed[u]) ed[u] = cnt;
++cnt, to[cnt] = u, nxt[cnt] = head[v], head[v] = cnt;
if (!ed[v]) ed[v] = cnt;
}
int f[maxn];
void init(int n) {
cnt = 0;
for (int i = 0; i <= n; i++) head[i] = ed[i] = 0, f[i] = i;
}
int findx(int x) {
return x == f[x] ? x : f[x] = findx(f[x]);
}
void unite(int x,int y) {
x = findx(x);
y = findx(y);
if (x == y) return ;
f[y] = x;
}
int num[maxn];
int main() {
int t;
scanf("%d", &t);
while (t--) {
int n, m;
scanf("%d%d", &n, &m);
init(n);
for (int i = 1; i <= m; i++) {
int u, v;
scanf("%d%d", &u, &v);
add(u + 1, v + 1);
}
int q;
scanf("%d", &q);
while (q--) {
int x, now = 0;
scanf("%d", &x);
x++;
if (x != findx(x)) continue;
for (int i = head[x]; i; i = nxt[i]) {
int v = to[i], fv = findx(v);
if(x == fv) continue;
num[++now]=fv;
unite(x,fv);
}
head[x]=0;
for (int i = 1; i <= now; i++) {
int v = num[i];
if (!head[v]) continue;
if (i == 1) head[x] = head[v], ed[x] = ed[v];
else nxt[ed[x]] = head[v], ed[x] = ed[v];
head[v] = 0;
}
}
for (int i = 1; i < n; i++) printf("%d ", findx(i) - 1);
printf("%d\n", findx(n) - 1);
}
return 0;
}