codeforces C. Customs Controls 2

C. Customs Controls 2
time limit per test2 seconds
memory limit per test512 megabytes
Look how short the statement is! This must be the easiest problem.

Given a directed acyclic graph 𝐺, you need to assign each vertex 𝑖 a positive integer weight 𝑤𝑖. Your goal is to make all paths from 1 to 𝑛 of equal length.

A directed acyclic graph is a graph with directed edges and without cycles.

The length of a path is defined as the sum of the weights of vertices on the path.

Input
The first line contains a positive integer 𝑇 (1≤𝑇≤104), denoting the number of test cases.

For each testcase:

The first line contains two integers 𝑛,𝑚 (1≤𝑛≤2⋅105, 1≤𝑚≤5⋅105), denoting the number of vertices and edges.
The next 𝑚 lines each contains two integers 𝑢, 𝑣, denoting an edge from 𝑢 to 𝑣.
It is guaranteed that ∑𝑛≤2⋅105, ∑𝑚≤5⋅105.

It is guaranteed that the graph contains no multiple edges, no self-loops and no cycles. It is also guaranteed that every vertex is reachable from 1 and can reach 𝑛.

Output
For each testcase, if there is no solution, then output "No" on a single line. Otherwise, output "Yes" on the first line, then 𝑛 positive integers 𝑤1,𝑤2,…,𝑤𝑛 (1≤𝑤𝑖≤109) on the second line.
本题要求判断能否通过对点赋权值来实现使从起点1到n点的所有路径的长度和一致
题目告知我们这是一个无环的有向图,并且本题的核心判断是能不能构造每条路径的路径和都一致的图。
根据:如果图中有某个点 i,其在某条路径上让这条路径比其他路径长或短,那就不能统一所有路径的长度。
所以可以通过从 1 到 i 的最短路径 和 从 i 到 n 的最长路径 构造出一条从 1 到 n 的完整路径来判断是否能够构造。

点击查看代码
int dp[MAXN], rdp[MAXN]; //1到i最短路径,i到n最长路径
int valid = 1;
        int target = dp[n];
        for (int i = 1; i <= n; i++) {
            if(dp[i] + rdp[i] - 1 != target){ //减 1 是因为 i 被重复计算
                valid = 0;
                break;
            }
        }

因为这是一个无环有向图,我们通过正向和反向两次拓扑排序来获取dp[i]和rdp[i],考虑到内存与问题处理,选择bfs的拓扑也就是kahn算法

点击查看代码
//正向拓扑
void topo_f(){
    int front = 0, rear = 0;
    queue[rear++] = 1;
    dp[1] = 1; //因为是点上的权值所以起点要算
    while(front < rear){
        int u = queue[front++];
        for(int i = head[u]; i; i = edges[i].next){
            int v = edges[i].to;
            if(dp[v] > dp[u] + 1){ //尝试用u更新v的最短路径长度
                dp[v] = dp[u] + 1;
            }
            if (--indeg[v] == 0){ //前驱为0时可以入对
                queue[rear++] = v;
            }
        }
    }
}

//反向拓扑
void topo_b(){
    int front = 0, rear = 0;
    queue[rear++] = n; //从最后开始BFS
    rdp[n] = 1;
    while(front < rear){
        int u = queue[front++];
        for(int i = rh[u]; i; i = redges[i].next){
            int v = redges[i].to;
            if(rdp[v] < rdp[u] + 1){ //更新v的最长路径长度
                rdp[v] = rdp[u] + 1;
            }
            if(--outdeg[v] == 0){
                queue[rear++] = v;
            }
        }
    }
}

完整代码

点击查看代码
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#define MAXN 500005

struct Edge{
    int to,next;
};

struct Edge edges[MAXN],redges[MAXN];;
int n, m;
int head[MAXN], rh[MAXN]; //正向和反向的邻接表表头
int tot, rtot; //正向边总数与反向边总数
int indeg[MAXN], outdeg[MAXN]; //正向图中i的入度,反向图中i的出度
int dp[MAXN], rdp[MAXN]; //1到i最短,i到n最长
int queue[MAXN];

//初始化图结构与路径状态
void init(int n){
    tot = rtot = 0;
    for(int i = 1; i <= n; i++){
        head[i] = rh[i] = 0;
        indeg[i] = outdeg[i] = 0;
        dp[i] = 1e9;
        rdp[i] = 0;
    }
}

void add_edge(int u, int v){
    edges[++tot].to = v;
    edges[tot].next = head[u];
    head[u] = tot;
    indeg[v]++; //每次插入边后节点v的入度加1

    redges[++rtot].to = u;
    redges[rtot].next = rh[v];
    rh[v] = rtot;
    outdeg[u]++; //每次插入边后节点u的出度加1
}

//正向拓扑
void topo_f(){
    int front = 0, rear = 0;
    queue[rear++] = 1;
    dp[1] = 1; //因为是点上的权值所以起点要算
    while(front < rear){
        int u = queue[front++];
        for(int i = head[u]; i; i = edges[i].next){
            int v = edges[i].to;
            if(dp[v] > dp[u] + 1){ //尝试用u更新v的最短路径长度
                dp[v] = dp[u] + 1;
            }
            if (--indeg[v] == 0){ //前驱为0时可以入对
                queue[rear++] = v;
            }
        }
    }
}

//反向拓扑
void topo_b(){
    int front = 0, rear = 0;
    queue[rear++] = n; //从最后开始BFS
    rdp[n] = 1;
    while(front < rear){
        int u = queue[front++];
        for(int i = rh[u]; i; i = redges[i].next){
            int v = redges[i].to;
            if(rdp[v] < rdp[u] + 1){ //更新v的最长路径长度
                rdp[v] = rdp[u] + 1;
            }
            if(--outdeg[v] == 0){
                queue[rear++] = v;
            }
        }
    }
}

int main(){
    int t;
    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_edge(u, v); //有向图
        }

        topo_f();
        topo_b();

        int valid = 1;
        int target = dp[n];
        for (int i = 1; i <= n; i++) {
            if(dp[i] + rdp[i] - 1 != target){ //检查能否使得每条路径的路径和一致
                valid = 0;
                break;
            }
        }
        if(!valid){
            printf("No");
        }else{
            printf("Yes");
            for(int i = 1; i <= n; i++){
                printf("1%c", i == n ? '\n' : ' ');
            }
        }
    }
    return 0;
}
posted @ 2025-06-21 00:22  sirro1uta  阅读(21)  评论(0)    收藏  举报