[题解]P11352 [NOISG 2024 Finals] Coin

思路

考虑图论建模,连接 \(x \to y\) 边,表示 \(rk_a < rk_b\)

那么一个点 \(u\)\(rk\) 在某一个时刻能被确定,则表示在当前图上所有点要么能走到 \(u\),要么能被 \(u\) 走到。进一步的,所有 \(rk_v\) 小于 \(rk_u\) 的点都能走到 \(u\),所有 \(rk_v\) 大于 \(rk_u\) 的点都能被 \(u\) 走到。

继续刻画这个性质,将所有边连起来,对这个 DAG 跑拓扑排序得到一个拓扑序,记作 \(dfn_i\)。注意到拓扑序有一个很好的性质,若 \(rk_u < rk_v\),则一定有 \(dfn_u < dfn_v\);反之有 \(dfn_u > dfn_v\)。于是 check \(rk_u\) 在当前图上是否能被确定,可以转化为 check \(dfn_v\) 小于 \(u\)\(v\) 能否走到 \(u\)\(dfn_v\) 大于 \(u\)\(v\) 能否被 \(u\) 走到。

由于任意时刻图都是一个 DAG 森林,所以对于前者 \(u\)\(dfn_v \leq dfn_u\) 的点构成子图中唯一一个出度为 \(0\) 的点;反之是唯一一个入度为 \(0\) 的点。这两个问题是对称的,下面只讨论一种情况的解法。

\(dfn_i\) 扫描线,指针扫到 \(t\) 的时候将满足 \(dfn_x,dfn_y \leq t\) 的所有 \((x,y)\) 边加入,维护每一个点第一次有出度的时刻,可以用线段树/优先队列等数据结构维护。复杂度 \(\Theta(n \log n)\)

Code

#include <bits/stdc++.h>
#define re register
#define fst first
#define snd second
#define chmin(a,b) (a = min(a,b))

using namespace std;

typedef pair<int,int> pii;
const int N = 8e5 + 10;
const int inf = 1e9 + 10;
int n,m;
pii E[N];
vector<int> g[N];
vector<pii> to1[N],to2[N];
int ans1[N],ans2[N];
int tim,deg[N],dfn[N],pid[N];

inline int read(){
    int r = 0,w = 1;
    char c = getchar();
    while (c < '0' || c > '9'){
        if (c == '-') w = -1;
        c = getchar();
    }
    while (c >= '0' && c <= '9'){
        r = (r << 3) + (r << 1) + (c ^ 48);
        c = getchar();
    }
    return r * w;
}

inline void topsort(){
    queue<int> q;
    for (re int i = 1;i <= n;i++){
        if (!deg[i]) q.push(i);
    } assert(q.size());
    while (!q.empty()){
        int u = q.front(); q.pop();
        pid[dfn[u] = ++tim] = u;
        for (int v:g[u]){
            if (!(--deg[v])) q.push(v);
        }
    }
}

struct{
    #define ls(u) (u << 1)
    #define rs(u) (u << 1 | 1)
    #define mid (tr[u].l + tr[u].r >> 1)

    struct node{
        int l,r,Max;
    }tr[N << 2];

    inline void pushup(int u){ tr[u].Max = max(tr[ls(u)].Max,tr[rs(u)].Max); }

    inline void build(int u,int l,int r){
        tr[u] = {l,r,inf};
        if (l == r) return;
        build(ls(u),l,mid); build(rs(u),mid + 1,r);
    }

    inline void update(int u,int x,int k){
        if (tr[u].l == tr[u].r) return (chmin(tr[u].Max,k)),void();
        if (x <= mid) update(ls(u),x,k);
        else update(rs(u),x,k);
        pushup(u);
    }

    inline int query(int u,int l,int r){
        if (l > r) return 0;
        if (l <= tr[u].l && tr[u].r <= r) return tr[u].Max;
        if (l <= mid && r > mid) return max(query(ls(u),l,r),query(rs(u),l,r));
        else if (l <= mid) return query(ls(u),l,r);
        else return query(rs(u),l,r);
    }

    #undef ls
    #undef rs
    #undef mid
}T;

int main(){
    n = read(),m = read();
    fill(ans1,ans1 + n + 3,inf);
    fill(ans2,ans2 + n + 3,inf);
    for (re int i = 1,a,b;i <= m;i++){
        a = read(),b = read();
        g[a].push_back(b);
        E[i] = {a,b}; deg[b]++;
    } topsort(); T.build(1,1,n);
    for (re int i = 1;i <= m;i++){
        to1[dfn[E[i].fst]].push_back({dfn[E[i].snd],i});
        to2[dfn[E[i].snd]].push_back({dfn[E[i].fst],i});
    } T.build(1,1,n);
    for (re int u = 1;u <= n;u++){
        for (pii v:to2[u]) T.update(1,v.fst,v.snd);
        ans1[pid[u]] = T.query(1,1,u - 1);
    } T.build(1,1,n);
    for (re int u = n;u;u--){
        for (pii v:to1[u]) T.update(1,v.fst,v.snd);
        ans2[pid[u]] = T.query(1,u + 1,n);
    }
    for (re int i = 1;i <= n;i++){
        int val = max(ans1[i],ans2[i]);
        printf("%d ",(val >= inf) ? -1 : val);
    }
    return 0;
}
posted @ 2026-01-05 16:35  WBIKPS  阅读(1)  评论(0)    收藏  举报