https://codeforces.com/gym/104053/problem/C
题目大意:有一个n个点m条边的有向无环图,要求从1号点到n号点的所有路径的带权路径长度相同,问每个点的点权应该是多少
1<=n<=2e5;1<=m<=5e5
思路:我们发现,对于每一个有多条入边的点,所有到该点的父结点的路径长度应该相等,这样到达该点的所有路径长度就都是相等的,那么我们可以把这样的点的所有父结点用并查集缩点,然后构建新的图,再重新判断这个图是否是无环图,同时,如果两个点有相同的子节点,但这两个点之间也有连边的话,也是没法使所有路径长度相等的,然后找出1到每个点边权为1时的最长路,也就是到每个点最多要经过多少个点,然后让每一条到n的路的点权和都等于到n的最多点数即可,也就是对于每两个节点u,v 令v的点权等于到v的最长路-到u的最长路
#include<iostream>
#include<cstdio>
#include<vector>
#include<map>
#include<queue>
using namespace std;
const int N = 2e5 + 5;
int head[N], cnt = 0;
struct Edge
{
int u, v, w, next;
}e[500005];
void addedge(int u, int v, int w)
{//链式前向星存图
e[++cnt].u = u;
e[cnt].v = v;
e[cnt].w = w;
e[cnt].next = head[u];
head[u] = cnt;
}
vector<int>e1[N], e2[N];
int n, m;
int fa[N], ans[N];
bool vis1[N], vis2[N];
int dis[N];
void init()
{
cnt = 0;
for (int i = 1; i <= n; i++)
{
vis1[i] = vis2[i] = 0;
head[i] = -1;
e1[i].clear();
e2[i].clear();
fa[i] = i;
dis[i] = 0;
}
}
int find(int x)
{//并查集
return fa[x] == x ? x : find(fa[x]);
}
bool dfs(int u)
{//判断有没有环
if (vis1[u])
return 0;//记录先序遍历访问过的节点,如果当前点被访问过,说明出现了环
if (vis2[u])
return 1;//记录后序遍历访问过的节点
vis1[u] = 1;
for (int i = head[u]; ~i; i = e[i].next)
{
int v = e[i].v;
if (!vis2[v])
{
if (!dfs(v))
return 0;
}
}
vis1[u] = 0;//后序遍历回溯时重置先序遍历过的点
vis2[u] = 1;
return 1;
}
void bfs()
{
dis[1] = 1;
priority_queue<pair<int, int> >q;//长度从大到小排序
q.push(make_pair(1, 1));
while (!q.empty())
{
int u = q.top().second, d = q.top().first;
q.pop();
for (int i = head[u]; ~i; i = e[i].next)
{
int v = e[i].v;
if (dis[v] < dis[u] + 1)
{//求最长路
dis[v] = dis[u] + 1;
q.push(make_pair(dis[v], v));
}
}
}
queue<int>qu;//再进行一次bfs求所有点的点权
qu.push(1);
ans[1] = 1;
while (!qu.empty())
{
int u = qu.front();
qu.pop();
if (vis1[u])
continue;
vis1[u] = 1;
for (int i = 0; i < e1[u].size(); i++)
{
int v = e1[u][i];
ans[v] = dis[find(v)] - dis[find(u)];//子节点和父节点相差的长度
qu.push(v);
}
}
}
int main()
{
int t;
cin >> t;
while (t--)
{
scanf("%d%d", &n, &m);
init();
for (int i = 1; i <= m; i++)
{
int u, v;
scanf("%d%d", &u, &v);
e1[u].push_back(v);//正向存的原始图
e2[v].push_back(u);//反向图
}
for (int i = 1; i <= n; i++)
{
if (e2[i].empty())
continue;
int u = e2[i][0];
for (int j = 0; j < e2[i].size(); j++)
{
fa[find(e2[i][j])] = fa[find(u)];//对于反向图中的每一个点,将他的所有子节点放入一个并查集中
}
}
map<pair<int, int>, int>ma;
bool flag = 1;
for (int i = 1; i <= n; i++)
{
int u = find(i);
for (int j = 0; j < e1[i].size(); j++)
{
int v = find(e1[i][j]);
if (u == v)//两个有共同子节点的点之间有连边
{
flag = 0;
break;
}
if (ma.find(make_pair(u, v)) == ma.end())
{//新图中不计重边
addedge(u, v, 1);
ma[make_pair(u, v)] = 1;
}
}
if (!flag)
break;
}
if (!flag||!dfs(1))
{
printf("No\n");
continue;
}
printf("Yes\n");
for (int i = 1; i <= n; i++)
{
vis1[i] = 0;
}
bfs();
for (int i = 1; i <= n; i++)
{
printf("%d ", ans[i]);
}
printf("\n");
}
return 0;
}