题目描述
输入描述:
输出描述:
示例1
输入
5
4 3
0 1
1 2
2 3
4
0 1 3 0
4 3
0 1
1 2
2 3
2
0 2
4 3
0 1
1 2
2 3
2
0 3
4 1
1 3
1
2
5 5
0 1
0 2
1 2
1 3
3 4
3
4 4 0
输出
0 0 0 0
2 2 2 2
0 0 3 3
0 1 2 3
0 0 0 0 0
说明
题目大意
给定一张n个点,m条边的图。每个点都有各自的颜色1,2,3……n。接下来进行q次染色操作。每次操作给定一个qi,要求把所有的颜色为qi的点的相邻的所有颜色不为qi的点,染色为qi。
分析
题意有点绕,接下来深解一下题意。其实就是对于每个颜色进行一次BFS式的扩展,如图。
首先分析染色的过程,每个点的周边部分被染色的过程就像并查集的过程,把周围的颜色变成这个点的颜色。
所以我们可以定义一个col[],表示每个点的颜色,如果被染色,那就用并查集查找一下它的颜色,然后更改。
但是这样有点问题。就是上图中的第二个图。所有被染色的点都要作为源点进行染色。(样例里面能调出来,太良心了)因此需要一个vector的合并,在染色的同时,把连边的信息也要同时转移过去。
于是这里就要用到一个启发式合并,把小的集合复制到大的集合里去,也叫拍脑瓜合并。
WAWAWA
拒绝骚操作,从我做起。 之前翻大佬代码的时候看到如此操作
for(scanf("%d",&t);t--;)
结果……被xinjun笑了一顿……
还有,并查集时千万记得路径压缩,否则T上天。(大概是好久不打并查集的锅……)
代码
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int MAXN=8e5+10;
int col[MAXN];
int gcl(int x){return col[x]==x?x:col[x]=gcl(col[x]);}//并查集,路径压缩!!
vector<int> vec[MAXN];
int main()
{
int t,n,m,q,qq,x,y;
scanf("%d",&t);
while(t--){
scanf("%d%d",&n,&m);
for(int i=0;i<=n;i++) vec[i].clear(),col[i]=i;//初始化
for(int i=1;i<=m;i++){
scanf("%d%d",&x,&y);
vec[x].push_back(y);
vec[y].push_back(x);
}//存边
scanf("%d",&q);
for(int i=1;i<=q;i++){
scanf("%d",&qq);
if(gcl(qq)==qq){//如果qq这种颜色还有,则可以染色
vector<int> now=vec[qq];//把vec[qq]取出来,防止循环出错
vec[qq].clear();
for(int j=0;j<now.size();j++){
int nxt=gcl(now[j]);
if(nxt!=qq){
col[nxt]=qq;//染色
if(vec[nxt].size()>vec[qq].size()) swap(vec[nxt],vec[qq]);
for(int k=0;k<vec[nxt].size();k++) vec[qq].push_back(vec[nxt][k]);
//启发式合并,复制小的
}
}
}
}
for(int i=0;i<n;i++) printf("%d ",gcl(i));printf("\n");
}
}
END
WA的概率与骚操作的多少成正比。