【洛谷P3573】RAJ-Rally
阿新 • • 發佈:2020-11-23
題目
題目連結:https://www.luogu.com.cn/problem/P3573
請找到一個點,使得刪掉這個點後剩餘的圖中的最長路徑最短。
思路
先跑兩遍拓撲排序求出以每一個點 \(x\) 結尾 / 開始的最長路長度 \(maxd[0/1][x]\)。
記 \(rk[i]\) 表示拓撲序在第 \(i\) 位的點 \(x\),對於刪去的點 \(x\),最長路徑長度應該是
按順序列舉拓撲序,假設列舉到第 \(i\) 位,我們在處理完第 \(i-1\) 位的時候將上面所有可能成為最長路的路徑長度扔到一個 multiset 中,如果刪除 \(rk[i]\),那麼同時要刪除的邊即為 \(maxd[0][rk[i]]\) 和 \(maxd[0][rk[i]]+maxd[1][rk[j]]+1\ (j<i\) 且存在一條 \(j\) 到 \(i\) 的邊 \()\)。
然後更新答案,再將 \(maxd[1][rk[i]]\) 和 \(maxd[1][rk[i]]+maxd[0][rk[j]]+1\ (j>i\) 且存在一條 \(i\) 到 \(j\) 的邊 \()\)
時間複雜度 \(O(n\log n)\)。
程式碼
#include <bits/stdc++.h> using namespace std; const int N=500010,M=1000010; int n,m,tot,ans0,ans1,head[2][N],maxd[2][N],deg[2][N],rk[N]; multiset<int> s; struct edge { int next,to; }e[M*2]; void add(int from,int to,int id) { e[++tot].to=to; e[tot].next=head[id][from]; head[id][from]=tot; } void topsort(int id) { tot=0; queue<int> q; for (int i=1;i<=n;i++) if (!deg[id][i]) q.push(i); while (q.size()) { int u=q.front(); q.pop(); rk[++tot]=u; for (int i=head[id][u];~i;i=e[i].next) { int v=e[i].to; deg[id][v]--; maxd[id][v]=max(maxd[id][v],maxd[id][u]+1); if (!deg[id][v]) q.push(v); } } } int main() { freopen("graph.in","r",stdin); freopen("graph.out","w",stdout); memset(head,-1,sizeof(head)); scanf("%d%d",&n,&m); for (int i=1,x,y;i<=m;i++) { scanf("%d%d",&x,&y); add(x,y,0); deg[0][y]++; add(y,x,1); deg[1][x]++; } topsort(0); topsort(1); for (int i=1;i<=n;i++) s.insert(maxd[0][i]); ans1=2e9; for (int i=1;i<=n;i++) { int x=rk[i]; s.erase(s.find(maxd[0][x])); for (int j=head[0][x];~j;j=e[j].next) s.erase(s.find(maxd[0][x]+maxd[1][e[j].to]+1)); if (s.size() && *s.rbegin()<ans1) ans1=*s.rbegin(),ans0=x; s.insert(maxd[1][x]); for (int j=head[1][x];~j;j=e[j].next) s.insert(maxd[1][x]+maxd[0][e[j].to]+1); } printf("%d %d\n",ans0,ans1); return 0; }