[题解]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;
}

浙公网安备 33010602011771号