2022 CCPC 广州站 个人题解C. Customs Controls 2

Analysis

传送门:https://codeforces.com/gym/104053/problem/C

容易观察到一个限制:对于连向同一个点 V V V的所有点 U i U_i Ui,其到起点的路径和一定完全相等。

111

那么建反图并查集缩点后按照拓扑序对每条边赋值后差分就能求得合法的点权。

Code

#include <bits/stdc++.h>
#define int long long
#define endl '\n'
using namespace std;

const int N = 3e5 + 10;

#define yes cout << "Yes\n"
#define no cout << "No\n"

struct UnionFind{
    std::vector<int> par, rank, size;
    int c;
    UnionFind() {}
    UnionFind(int n) : par(n + 10), rank(n, 0), size(n, 1), c(n){
        for (int i = 0; i <= n + 1; ++i) par[i] = i;
    }
    int find(int i) { return (par[i] == i ? i : (par[i] = find(par[i]))); }
    bool same(int i, int j) { return find(i) == find(j); }
    int get_size(int i) { return size[find(i)]; }
    int count() { return c; }
    void merge(int i, int j){
        par[find(i)] = par[find(j)];
    }
} uf;

vector<int> g[N], invg[N], newg[N];

void solve(){
    int n, m; cin >> n >> m;
    for(int i = 1; i <= n; i++) {
        g[i].clear(); invg[i].clear(); newg[i].clear();
    }
    uf = UnionFind(n + 1);
    for (int i = 1; i <= m; i++){
        int u, v; cin >> u >> v;
        g[u].push_back(v);
        invg[v].push_back(u);
    }
    for (int i = 1; i <= n; i++){
        if (invg[i].empty()) continue;
        int now = invg[i][0];
        for (int j = 1; j < invg[i].size(); j++){
            int u = uf.find(now), v = uf.find(invg[i][j]);
            if (u != v) uf.merge(u, v);
        }
    }
    vector<int> in(n + 1, 0), dep(n + 1, 1);
    for (int i = 1; i <= n; i++){
        for (int v : g[i]){
            newg[uf.find(i)].push_back(uf.find(v));
            in[uf.find(v)]++;
        }
    }
    queue<int> q;
    for (int i = 1; i <= n; i++){
        if (i != uf.find(i)) in[i] = -1;
        else if (in[i] == 0) q.push(i);
    }
    while (q.size()){
        int now = q.front();
        q.pop();
        for (int nex : newg[now]){
            if (--in[nex] == 0){
                q.push(nex);
                dep[nex] = dep[now] + 1;
            }
        }
    }
    int f = 1;
    for (int i = 1; i <= n; i++) if (in[i] > 0) f = 0;
    if (f == 0){ no; return; }
    yes;
    vector<int> ans(n + 1, 1);
    for (int i = 2; i < n; i++){
        int nex = invg[i][0];
        ans[i] = dep[uf.find(i)] - dep[uf.find(nex)];
    }
    for(int i = 1; i <= n; i++) cout << ans[i] << " \n"[i == n];
}

signed main() {
    ios_base::sync_with_stdio(false), cin.tie(0);
    int t = 1; cin >> t;
    while (t--) solve();
    return 0;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

HeartFireY

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值