洛谷P3573 [POI2014]RAJ-Rally(BZOJ3832)
阿新 • • 發佈:2018-12-18
拓撲排序 堆
妙蛙
注意到這是一個DAG,那麼我們可以一遍拓排求出從起點到為最長路和到終點的最長路(s向所有入度為0的點連邊,所有出度為0的點向t連邊)。若一條最長路經過,那麼必有。
我們按照拓撲序列舉每個點。每次把關於它的最長路從集刪掉並更新答案,然後把它們加到集裡。因此要維護的就是三個操作:刪除一個數,插入一個數和查詢最大值。用堆即可。
程式碼:
#include<queue>
#include<cctype>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 500005
#define F inline
using namespace std;
struct edge{ int nxt,to; }ed[N<<1];
int n,m,k,h1[N],h2[N],ds[N],dt[N],in[N],que[N];
struct Queue{
priority_queue <int> q,d;
F void push(int x){ q.push(x); }
F void erase(int x){ d.push(x); }
F void pop(){
while (!d.empty()&&q.top()==d.top())
q.pop(),d.pop(); q.pop();
}
F int top(){
while (!d.empty()&&q.top()==d.top())
q.pop(),d.pop(); return q.top();
}
}q;
F char readc(){
static char buf[100000],*l=buf,*r=buf;
if (l==r) r=(l=buf)+fread(buf,1,100000,stdin);
return l==r?EOF:*l++;
}
F int _read(){
int x=0; char ch=readc();
while (!isdigit(ch)) ch=readc();
while (isdigit(ch)) x=(x<<3)+(x<<1)+(ch^48),ch=readc();
return x;
}
F void tp(){
int l=0,r=0;
for (int i=1;i<=n;i++) if (!in[i]) que[++r]=i;
while (l<r)
for (int x=que[++l],i=h1[x];i;i=ed[i].nxt)
if (!(--in[ed[i].to])) que[++r]=ed[i].to;
for (int i=1;i<=n;i++)
for (int x=que[i],j=h1[x];j;j=ed[j].nxt)
ds[ed[j].to]=max(ds[ed[j].to],ds[x]+1);
for (int i=n;i;i--)
for (int x=que[i],j=h2[x];j;j=ed[j].nxt)
dt[ed[j].to]=max(dt[ed[j].to],dt[x]+1);
}
#define add(h,x,y) ed[++k]=(edge){h[x],y},h[x]=k
int main(){
n=_read(),m=_read();
for (int i=1,x,y;i<=m;i++)
x=_read(),y=_read(),add(h1,x,y),add(h2,y,x),in[y]++;
tp(); int id,ans=2e9;
for (int i=1;i<=n;i++) q.push(dt[i]);
for (int i=1;i<=n;i++){
int x=que[i]; q.erase(dt[x]);
for (int j=h2[x];j;j=ed[j].nxt)
q.erase(ds[ed[j].to]+dt[x]+1);
if (q.top()<ans) ans=q.top(),id=x;
for (int j=h1[x];j;j=ed[j].nxt)
q.push(ds[x]+dt[ed[j].to]+1);
q.push(ds[x]);
}
return printf("%d %d",id,ans),0;
}