题目描述
给定一个N个点M条边的有向无环图,每条边长度都是1。
请找到一个点,使得删掉这个点后剩余的图中的最长路径最短。
题解
暴力O(n^2)只跑最长路上的点有80分
对于这题我们很容易想到拓扑,然后就什么也想不到了。
题解是我们把点分成S和T,其中所有的答案都在S里面,T里面,S->T里面。不过我先来口胡一下正常神犇思路。
显然我们要维护一些东西。
- 思考每一个点带来的可能答案是什么样的,因为是删点,肯定要把答案放到点上。记 分别表示进去的最长和出去的最长,那么答案就是对于每一条边 。
- 思考这道题有什么特殊的性质,DAG的特色就是拓扑图。思考这道题有什么好
乱搞的,好像只有删点的顺序,思考有什么有特色的删点顺序,值序,拓扑序。假如我们思考了一会,发现值序没什么用,那我们就推一下拓扑序的性质。 - 思考怎么维护,每一次删点,我们不仅要让和自己直接有关的值消失,还要让那些路过这个点的值消失,这实在是太麻烦了,如果我们思考所有的边那么一辈子都想不出来的,还好我们已经把答案转化了。那么拓扑序有什么用呢?思考一下如果我们在拓扑图中拿掉一个点,那么只有在这个点之后的点的 前面的点的 和在这个点之前的 后面的点的 ,那就很显然分两块搞了呀
- 按拓扑序一个一个从右边扔到左边,开个值域线段树记有没有这个值,和最大值是多少。先把所有 扔进去,每次搬点的时候扔一些出来,扔一些进去。具体见代码,其实就是上述思想的实现,还是很好想的
代码
#include <bits/stdc++.h>
#define maxn 1000005
#define MAXN 2000005
#define lo (o<<1)
#define ro (o<<1|1)
#define mid (l+r>>1)
#define INF 0x3f3f3f3f
#define LL long long
using namespace std;
int read(){
int res,f=1; char c;
while(!isdigit(c=getchar())) if(c=='-') f=-1; res=(c^48);
while(isdigit(c=getchar())) res=(res<<3)+(res<<1)+(c^48);
return res*f;
}
struct EDGE{
int u,v,nxt;
}e[MAXN<<1];
int cnt,head[maxn],ans,ans_point;
int T,n,m,pre[maxn],indeg[maxn],outdeg[maxn],dis_1[maxn],dis_n[maxn],vis[maxn];
queue<int> Q,P;
void add(int u,int v){
e[++cnt]=(EDGE){u,v,head[u]};
head[u]=cnt;
}
int num[maxn],mx[maxn];
void update(int o,int l,int r,int pos,int w){
if(l==r){
num[o]+=w;
if(num[o]>0) mx[o]=pos;
else num[o]=0,mx[o]=0;
return ;
}
if(pos<=mid) update(lo,l,mid,pos,w);
else update(ro,mid+1,r,pos,w);
mx[o]=max(mx[lo],mx[ro]);
}
int main(){
T=read();
while(T--){
cnt=1; ans=INF; ans_point=0;
memset(mx,0,sizeof mx);
memset(num,0,sizeof num);
memset(head,-1,sizeof head);
memset(indeg,0,sizeof indeg);
memset(outdeg,0,sizeof outdeg);
memset(dis_1,0,sizeof dis_1);
memset(dis_n,0,sizeof dis_n);
n=read(); m=read();
if(m<=1) {puts("1 0"); continue;}
for(int i=1,u,v;i<=m;i++){
u=read(); v=read(); add(u,v); add(v,u);
outdeg[u]++; indeg[v]++;
}
for(int i=1;i<=n;i++) if(!indeg[i]) Q.push(i);
while(!Q.empty()){
int u=Q.front(); P.push(Q.front()); Q.pop();
for(int i=head[u];~i;i=e[i].nxt){
if(i&1) continue;
int v=e[i].v; indeg[v]--;
if(dis_1[u]+1>dis_1[v]) dis_1[v]=dis_1[u]+1;
if(!indeg[v]) Q.push(v);
}
}
for(int i=1;i<=n;i++) if(!outdeg[i]) Q.push(i);
while(!Q.empty()){
int u=Q.front(); Q.pop();
for(int i=head[u];~i;i=e[i].nxt){
if(!(i&1)) continue;
int v=e[i].v; outdeg[v]--;
if(dis_n[u]+1>dis_n[v]) dis_n[v]=dis_n[u]+1;
if(!outdeg[v]) Q.push(v);
}
}
for(int i=1;i<=n;i++) update(1,0,n,dis_n[i],1);
while(!P.empty()){
int u=P.front(); P.pop();
for(int i=head[u];~i;i=e[i].nxt){
if(!(i&1)) continue;
update(1,0,n,dis_n[u]+1+dis_1[e[i].v],-1);
}
update(1,0,n,dis_n[u],-1);
if(mx[1]<ans || ans_point>u && mx[1]==ans) ans=mx[1],ans_point=u;
for(int i=head[u];~i;i=e[i].nxt){
if(i&1) continue;
update(1,0,n,dis_1[u]+1+dis_n[e[i].v],1);
}
update(1,0,n,dis_1[u],1);
}
printf("%d %d\n",ans_point,ans);
}
return 0;
}