[POI2014]RAJ(最短路,拓扑排序)

对于一个点 \(x\) 如何求答案?

由于这个图是个有向无环图,可以先拓扑排序一遍,求出每个点的拓扑序,从起点到它的最长路 \(d2\),从它到终点的最长路 \(d1\)。(我写代码是这么写的,注意顺序)

把拓扑序比小 \(x\) 的点的点集叫 \(A\),大的叫 \(B\)。答案就是 \(\max\limits{u\in A,v\in B}(d2_u+d1_v+w_{(u,v)})\)

发现当 \(x\) 的拓扑序变大 \(1\) 时,集合 \(A\) 会多一个数,集合 \(B\) 会少一个数。

可以动态维护最大值。

时间复杂度 \(O(m\log m)\)

#include<bits/stdc++.h>
using namespace std;
typedef pair<int,int> PII;
const int maxn=1000100;
#define MP make_pair
#define FOR(i,a,b) for(int i=(a);i<=(b);i++)
#define ROF(i,a,b) for(int i=(a);i>=(b);i--)
#define MEM(x,v) memset(x,v,sizeof(x))
inline int read(){
    int x=0,f=0;char ch=getchar();
    while(ch<'0' || ch>'9') f|=ch=='-',ch=getchar();
    while(ch>='0' && ch<='9') x=x*10+ch-'0',ch=getchar();
    return f?-x:x;
}
int n,m,el,ans1,ans2=2e9,head[maxn],to[maxn],nxt[maxn],el2,head2[maxn],to2[maxn],nxt2[maxn],deg[maxn],id[maxn],cnt,seq[maxn],q[maxn],h,r,d1[maxn],d2[maxn];
multiset<int,greater<int> > fuck;
inline void add(int u,int v){
    to[++el]=v;nxt[el]=head[u];head[u]=el;
}
inline void add2(int u,int v){
    to2[++el2]=v;nxt2[el2]=head2[u];head2[u]=el2;
}
int dfs(int u){
    if(~d1[u]) return d1[u];
    for(int i=head[u];i;i=nxt[i]) d1[u]=max(d1[u],dfs(to[i]));
    return ++d1[u];
}
int main(){
    n=read();m=read();
    FOR(i,1,m){
        int u=read(),v=read();
        add(u,v);add2(v,u);
        deg[v]++;
    }
    MEM(d1,-1);
    FOR(i,1,n) if(d1[i]==-1) d1[i]=dfs(i);
    h=1;r=0;
    FOR(i,1,n) if(!deg[i]) q[++r]=i,seq[id[i]=++cnt]=i;
    while(h<=r){
        int u=q[h++];
        for(int i=head[u];i;i=nxt[i]){
            int v=to[i];
            d2[v]=max(d2[v],d2[u]+1);
            if(!--deg[v]) q[++r]=v,seq[id[v]=++cnt]=v;
        }
    }
    FOR(i,1,n) fuck.insert(d1[i]);
    FOR(i,1,n){
        int u;
        if(i!=1){
            u=seq[i-1];
            for(int e=head[u];e;e=nxt[e]){
                int v=to[e];
                fuck.insert(d2[u]+1+d1[v]);
            }
            fuck.insert(d2[u]);
        }
        u=seq[i];
        for(int e=head2[u];e;e=nxt2[e]){
            int v=to2[e];
            multiset<int,greater<int> >::iterator it=fuck.find(d2[v]+1+d1[u]);
            fuck.erase(it);
        }
        multiset<int,greater<int> >::iterator it=fuck.find(d1[u]);
        fuck.erase(it);
        if(*fuck.begin()<ans2) ans1=seq[i],ans2=*fuck.begin();
    }
    printf("%d %d\n",ans1,ans2);
}

猜你喜欢

转载自www.cnblogs.com/1000Suns/p/11295993.html