题意:
现在有n个数,然后有一些有向边。现在需要将每个数涂上色,要求如下:
1.颜色种类尽可能多
2.如果两个点指向相同颜色的点,那么这两个点的颜色相同
3.最终构造出来的颜色按顺序输出时字典序最小
题解:
我们可以发现的就是如果有一个点的入度>=2那么这些儿子的点的颜色相同,然后再指向这些儿子的点的所有颜色相同,于是我们就需要dfs去做并查集,然后多个儿子的话,我们需要将儿子的所有儿子都放到根那里,此时就需要启发式合并,这样的时间复杂度最大就是
的。
直到做到某个点的儿子数<=1
#include<bits/stdc++.h>
using namespace std;
const int N=2e5+5;
int fa[N];
int finds(int x){return fa[x]<0?x:fa[x]=finds(fa[x]);}
vector<int>vec[N],tmp;
void dfs(int x){
if(vec[x].size()<2)return ;
int rt=finds(vec[x][0]);
for(int i=1;i<vec[x].size();i++){
int ne=finds(vec[x][i]);
if(ne==rt)continue;
if(fa[rt]<=fa[ne]){
fa[rt]+=fa[ne];
fa[ne]=rt;
for(auto j:vec[ne])
vec[rt].push_back(j);
vec[ne].clear();
}
else{
fa[ne]+=fa[rt];
fa[rt]=ne;
for(auto j:vec[rt])
vec[ne].push_back(j);
vec[rt].clear();
rt=ne;
}
}
vec[x].clear();
vec[x].push_back(rt);
dfs(rt);
}
int col[N];
int main()
{
memset(fa,-1,sizeof(fa));
int n,m,x,y;
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++)
scanf("%d%d",&x,&y),vec[x].push_back(y);
for(int i=1;i<=n;i++)
dfs(i);
int cnt=0;
for(int i=1;i<=n;i++){
int f=finds(i);
if(!col[f])
col[f]=++cnt;
printf("%d\n",col[f]);
}
return 0;
}