题意:
给一个 n 个点的 Graph,第 i 个点一刚开始是第 I 种颜色,接着有 k 次 操作,第 i 次操作有个参数 oi 代表颜色 oi 会侵略所有和自己相邻的颜色, 于是所有和 oi 相邻的颜色全都变成 oi (若已没有颜色oi 已被侵略,则该次 操作无效),求最终每个点的颜色。
思路:
重要观察:在所有操作过程中,对于每个点,至多只会有一次把相邻的点和 自己变为同一种颜色的操作,经过该次操作后,就永远和相邻的点同色了。
-
对于每个颜色都维护一个点的链表,储存该颜色中尚未把所有相邻 点变为自己颜色的点。
-
用并查集纪录每个点属于哪个颜色。
-
每次操作就会把颜色 oi 的链表中所有相邻的点所属的颜色都变为 oi , 就直接把对应的链表合并以及在并查集上做对应的操作。
code:
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e6 + 5;
struct node{
int u, v, next;
} g[maxn * 2];
int head[maxn], fa[maxn], cnt;
list<int> ls[maxn];
void add(int u, int v){
g[cnt].u = u;
g[cnt].v = v;
g[cnt].next = head[u];
head[u] = cnt++;
}
void init(int n){
cnt = 0;
for(int i= 0; i < n; i++){
head[i] = -1;
fa[i] = i;
ls[i].clear();
ls[i].push_back(i);
}
}
int find(int x){
return fa[x] == x ? x : fa[x] = find(fa[x]);
}
int main(){
int t, n, m, q;
scanf("%d", &t);
while(t--){
scanf("%d %d", &n, &m);
init(n);
for(int i = 0; i < m; i++){
int u, v;
scanf("%d %d", &u, &v);
add(v, u);
add(u, v);
}
scanf("%d", &q);
int a;
while(q--){
scanf("%d", &a);
if(fa[a] != a) continue;
int fu = find(a);
int cot = ls[a].size();
while(cot--){
int u = ls[a].front();
ls[fu].pop_front();
for(int i = head[u]; i != -1; i = g[i].next){
int v = g[i].v;
int fv = find(v);
if(fu != fv) {
fa[fv] = fu;
ls[a].splice(ls[a].end(), ls[fv]); //把链表ls[fv]接在ls[a]尾部
}
}
}
}
for(int i = 0; i < n; i++) printf("%d ", find(i));
printf("\n");
}
}