Rally

题目描述

给定一个N个点M条边的有向无环图,每条边长度都是1。

请找到一个点,使得删掉这个点后剩余的图中的最长路径最短。

题解

暴力O(n^2)只跑最长路上的点有80分
对于这题我们很容易想到拓扑,然后就什么也想不到了。
题解是我们把点分成S和T,其中所有的答案都在S里面,T里面,S->T里面。不过我先来口胡一下正常神犇思路。
显然我们要维护一些东西。

  1. 思考每一个点带来的可能答案是什么样的,因为是删点,肯定要把答案放到点上。记 d i s i n [ x ] , d i s o u t [ x ] dis_{in}[x],dis_{out}[x] 分别表示进去的最长和出去的最长,那么答案就是对于每一条边 d i s i n [ u ] + 1 + d i s o u t [ v ] dis_{in}[u]+1+dis_{out}[v]
  2. 思考这道题有什么特殊的性质,DAG的特色就是拓扑图。思考这道题有什么好乱搞的,好像只有删点的顺序,思考有什么有特色的删点顺序,值序,拓扑序。假如我们思考了一会,发现值序没什么用,那我们就推一下拓扑序的性质。
  3. 思考怎么维护,每一次删点,我们不仅要让和自己直接有关的值消失,还要让那些路过这个点的值消失,这实在是太麻烦了,如果我们思考所有的边那么一辈子都想不出来的,还好我们已经把答案转化了。那么拓扑序有什么用呢?思考一下如果我们在拓扑图中拿掉一个点,那么只有在这个点之后的点的 d i s o u t dis_{out} 前面的点的 d i s i n dis_{in} 和在这个点之前的 d i s i n + 1 + dis_{in}+1+ 后面的点的 d i s o u t dis_{out} ,那就很显然分两块搞了呀
  4. 按拓扑序一个一个从右边扔到左边,开个值域线段树记有没有这个值,和最大值是多少。先把所有 d i s o u t dis_{out} 扔进去,每次搬点的时候扔一些出来,扔一些进去。具体见代码,其实就是上述思想的实现,还是很好想的

代码

#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;
}

猜你喜欢

转载自blog.csdn.net/qq_32461955/article/details/83448447