[POI2014]RAJ-Rally
本题的要点是有向无环图,所以我们可以先拓扑排序,并
于是我们可以得到经过边
把拓扑序小于
所以我们要维护
那么我们怎么得到上面的路径?我们可以从删除
一开始数据结构中有:
删除
将
所以我们可以将初始状态设为所有节点都在
我们的数据结构要支持插入、删除、求最大值。所以我们可以用multiset,权值线段树。但我们还可以写堆。
我们可以用
struct Queue{
priority_queue<int>a,b;int sz;
void push(int x){a.push(x),++sz;}
void pop(int x){b.push(x),--sz;}
int top(){
while(!b.empty()&&a.top()==b.top())a.pop(),b.pop();
return sz>0?a.top():-INF;
}
}Q;//可删堆
下面我们来分析一下复杂度:初始化+拓扑排序
比用multiset和权值线段树都跑得慢,果然是人弱自带大常数。
代码如下:
#include<stdio.h>
#include<queue>
using namespace std;
int rd(){
int k=0,f=1;char c=getchar();
while(c>'9'||c<'0'){if(c=='-')f=0;c=getchar();}
while(c>='0'&&c<='9')k=k*10+c-'0',c=getchar();
return f?k:-k;
}
const int N=500001;
int Max(int x,int y){return x>y?x:y;}
int Min(int x,int y){return x<y?x:y;}
struct E{int v,nxt;}e[N<<1];
struct Queue{
priority_queue<int>a,b;
void push(int x){a.push(x);}
void pop(int x){b.push(x);}
int top(){while(!b.empty()&&a.top()==b.top())a.pop(),b.pop();return a.top();}
}Q;//可删堆
int n,m,h[N],H[N],cnt,u,v,ds[N],dt[N],I[N],a[N],T,w,ans;queue<int>q;
void add(int u,int v,int*head){e[++cnt].v=v,e[cnt].nxt=head[u],head[u]=cnt;}
int main(){
freopen("data.in","r",stdin);
freopen("data.out","w",stdout);
n=rd(),m=rd();
for(int i=1;i<=m;++i)u=rd(),v=rd(),add(u,v,h),add(v,u,H),++I[v];
for(int i=1;i<=n;++i)if(!I[i])q.push(i);
while(!q.empty()){
u=q.front(),q.pop(),a[++T]=u;
for(int i=h[u];i;i=e[i].nxt)if(--I[v=e[i].v]==0)q.push(v);
}//拓扑排序
for(int i=1;i<=n;++i){
u=a[i];
for(int i=h[u];i;i=e[i].nxt)v=e[i].v,dt[v]=Max(dt[v],dt[u]+1);
}
for(int i=n;i;--i){
u=a[i];
for(int i=H[u];i;i=e[i].nxt)v=e[i].v,ds[v]=Max(ds[v],ds[u]+1);
}//求dt,ds
for(int i=1;i<=n;++i)Q.push(ds[i]);
ans=Q.top();
for(int j=1;j<=n;++j){
u=a[j],Q.pop(ds[u]);
for(int i=H[u];i;i=e[i].nxt)Q.pop(dt[e[i].v]+ds[u]+1);//删除点和边
T=Q.top();
if(T<=ans)ans=T,w=u;//更新答案
for(int i=h[u];i;i=e[i].nxt)Q.push(dt[u]+ds[e[i].v]+1);
Q.push(dt[u]);//插入点和边
}
printf("%d %d\n",w,ans);
return 0;
}