传送门biu~
如果不删点的话,那就是很简单的一个dp。因为起终点不能确定,所以建一个超级源S和一个超级汇T。问题转化为求从S到T的最长路。
对于删点的问题,我们可以预处理S到每个点的最长路,和每个点到T的最长路,这样就可以给边赋上权值来代表经过这条边的最长路。将图按拓扑序分层,按拓扑序枚举每一个点。将这一个点连接的“上一层”的边删除,再将“下一层”的边加入,两个过程中间的状态就恰好是”这个点不在“的状态,这个时候记录最长路为答案。显然,如果加入的“下一层”的边正好是下一个点“上一层”的边,这条边也会被删除,也就是说不会有多条边同时代表同一条最长路的情况。
对于维护最值可以用带删除指定元素的Heap实现,也可以用权值线段树实现。这里写上Heap版:
#include<bits/stdc++.h>
using namespace std;
struct Heap{
priority_queue<int>a,b;
inline void update(){while(!a.empty() && !b.empty() && a.top()==b.top())a.pop(),b.pop();}
inline void del(int x){b.push(x);update();}
inline int top(){return a.top();}
inline void push(int x){a.push(x);update();}
}q;
int head[500005],fir[500005],ru[500005],nex[2000005],to[2000005],tp;
inline void add(int x,int y){
++ru[y];
nex[++tp]=head[x];
head[x]=tp;
to[tp]=y;
nex[++tp]=fir[y];
fir[y]=tp;
to[tp]=x;
}
int n,m,ans,len=1e9;
int topo[500005],S[500005],T[500005];
void init() {
queue<int>q;int cnt=0;
for(int i=1;i<=n;i++)
if(!ru[i]) q.push(i);
while(!q.empty()){
int x=q.front();q.pop();topo[++cnt]=x;
for(int i=head[x];i;i=nex[i])
if(!--ru[to[i]]) q.push(to[i]);
}
for(int j=1;j<=n;j++){
int x=topo[j];
S[x]=max(1,S[x]);
for(int i=head[x];i;i=nex[i]) S[to[i]]=max(S[to[i]],S[x]+1);
}
for(int j=n;j>=1;j--){
int x=topo[j];
T[x]=max(1,T[x]);
for(int i=head[x];i;i=nex[i]) T[x]=max(T[x],T[to[i]]+1);
}
}
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++) {
int u,v;
scanf("%d%d",&u,&v);
add(u,v);
}
init();
q.push(0);
for(int i=1;i<=n;i++) q.push(T[i]);
for(int j=1;j<=n;j++){
int x=topo[j];
for(int i=fir[x];i;i=nex[i]) q.del(S[to[i]]+T[x]);
q.del(T[x]);
if(q.top()<len) len=q.top(),ans=x;
for(int i=head[x];i;i=nex[i]) q.push(S[x]+T[to[i]]);
q.push(S[x]);
}
printf("%d %d\n",ans,len-1);
return 0;
}