并查集 + 链表 -- G Operating on a Graph

G Operating on a Graph

题意:
给一个 n 个点的 Graph,第 i 个点一刚开始是第 I 种颜色,接着有 k 次 操作,第 i 次操作有个参数 oi 代表颜色 oi 会侵略所有和自己相邻的颜色, 于是所有和 oi 相邻的颜色全都变成 oi (若已没有颜色oi 已被侵略,则该次 操作无效),求最终每个点的颜色。

思路:
重要观察:在所有操作过程中,对于每个点,至多只会有一次把相邻的点和 自己变为同一种颜色的操作,经过该次操作后,就永远和相邻的点同色了。

  1. 对于每个颜色都维护一个点的链表,储存该颜色中尚未把所有相邻 点变为自己颜色的点。

  2. 用并查集纪录每个点属于哪个颜色。

  3. 每次操作就会把颜色 oi 的链表中所有相邻的点所属的颜色都变为 oi , 就直接把对应的链表合并以及在并查集上做对应的操作。

code:

#include
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");
		
	
	}
	
}

你可能感兴趣的:(图论)