Rally
POI2014
題意
1.有一個N個點M條邊的有向無環圖,每條邊長度都是1
2.找到一個點,使得刪掉這個點後剩餘的圖中的最長路徑最短
3.問刪去的點哪個點,以及刪掉這個點後剩餘的圖中的最長路徑長度爲多少
解
1.存下每個點的入邊,出邊(分開存)
2.預處理每個點沿着出邊最長鏈是多少(dis2[x]),沿着入邊走最長鏈是多少(dis1[x])
3.先把所有點出邊最長鏈(dis2[x])放進multiset(到時候要查詢最大最小值)
4.按照入度拓撲排序,依次遍歷每個點
5.遍歷到x點時
先把能夠進入它的點(y)與它(x)組成的最長鏈都刪去
y->x 刪去dis1[y]+1+dis2[x]
再刪去x點的出邊最長鏈
刪去dis2[x]
6.此時multiset中的最大值就是刪去x點後的最長鏈長度
7.把能夠從它連出去的點(y)與(x)組成的最長鏈都加入
x->y 加入dis2[y]+1+dis1[x]
把x點的入邊最長鏈加入
加入dis1[x]
具體代碼
#include<bits/stdc++.h>
using namespace std;
const int M=500005;
int n,m,rdu[M],cdu[M],du[M];
int head[M],asdf,rhead[M],rasdf;
multiset<int>S;
multiset<int>::iterator it;
struct edge {
int to,nxt;
} G[M*2],rG[M*2];
void add_edge(int a,int b) {
G[++asdf].to=b;
G[asdf].nxt=head[a];
head[a]=asdf;
rG[++rasdf].to=a;
rG[rasdf].nxt=rhead[b];
rhead[b]=rasdf;
}
int dis1[M],dis2[M],q[M],l,r;
void init() {
l=r=1;
for(int i=1; i<=n; i++) {
if(!rdu[i])q[r++]=i;
}
while(l<r) {
int x=q[l++];
for(int i=head[x]; i; i=G[i].nxt) {
int y=G[i].to;
if(!(--rdu[y]))q[r++]=y;
dis1[y]=max(dis1[y],dis1[x]+1);
}
}
l=r=1;
for(int i=1; i<=n; i++) {
if(!cdu[i])q[r++]=i;
}
while(l<r) {
int x=q[l++];
for(int i=rhead[x]; i; i=rG[i].nxt) {
int y=rG[i].to;
if(!(--cdu[y]))q[r++]=y;
dis2[y]=max(dis2[y],dis2[x]+1);
}
}
}
void solve() {
int ans=1e9,px=0;
l=r=1;
for(int i=1; i<=n; i++) {
if(!du[i])q[r++]=i;
S.insert(dis2[i]);
}
while(l<r) {
int x=q[l++];
for(int i=rhead[x]; i; i=rG[i].nxt) {
int y=rG[i].to;
it=S.find(dis1[y]+1+dis2[x]);
if(it!=S.end())S.erase(it);
}
it=S.find(dis2[x]);
if(it!=S.end())S.erase(it);
if(S.size()) {
it=S.end();
it--;
if((*it)<ans) {
ans=*it;
px=x;
}
}
S.insert(dis1[x]);
for(int i=head[x]; i; i=G[i].nxt) {
int y=G[i].to;
if(!(--du[y]))q[r++]=y;
S.insert(dis2[y]+1+dis1[x]);
}
}
printf("%d %d\n",px,ans);
}
int main() {
int a,b;
scanf("%d %d",&n,&m);
for(int i=1; i<=m; i++) {
scanf("%d %d",&a,&b);
add_edge(a,b);
rdu[b]++,cdu[a]++;
du[b]++;
}
init();
solve();
return 0;
}