• 【BZOJ3832】Rally(POI2014)-拓扑排序+最长路+堆


    测试地址:Rally
    题目大意:一个n(5×105)个点,m(106)条边的DAG,要求删掉一个点使得图的最长路最短,要求找到这个点以及最短的最长路长。
    做法:本题需要用到拓扑排序+最长路+堆。
    我们通常使用的求DAG的最长路的方法是,对整个图进行拓扑序DP。然而放在这题里这样做就不行了,我们必须找到另一种求最长路的方法。
    我们可以仿照网络流,建一个源点和一个汇点,从源点向每个点连有向边,从每个点向汇点连有向边,那么DAG的最长路就转化成源点到汇点的最长路,只不过要减去2,因为这不影响大小关系,所以我们直接继续讨论。
    对于一条边(u,v),令val(x,0/1)为源点到x或者x到汇点的最长路,不难想到,过边(u,v)的最长路长度为val(u,0)+val(v,1)+1。但是整张图的最长路不一定过这条边,于是我们可以维护一个边集,使得图的最长路一定经过其中的边,不难发现一个割集就满足这样的条件。把上面那个式子看做边的权值,那么求整张图的最长路就是求对应割集的边权最大值。
    找到了另一种求最长路的方法,我们就可以考虑删掉一个点的情况了。因为删掉一个点后,指向这个点或者从这个点指出的边都会被同时删除。那么我们需要维护一个割集,并且这个割集必须满足一个条件:删掉该点后,割集中涉及到的所有点权没有发生变化。
    为了方便,我们设这个割集把点分成S,T两个集合,分别表示源点和汇点所在的集合。枚举要删除的点,要保证割集中的边两端的点权不发生变化,即要保证:删除的点的拓扑序不在S集中有边指向T集的点之前,删除的点的拓扑序不在T集中有边从S集指向的点之后。这个条件看似复杂,实际上要满足这个条件很简单:只需要保证删掉的点是S集中拓扑序最大的点就行了。因此我们一开始令所有点都在T集中(除了源点),然后按拓扑序枚举要删除的点,那么删除这一点就要把割集中与它相关的边全部删掉,求出割集中边权的最大值后,把这个点归入S集中,并把从它出发的所有边加入到割集中。
    注意到删除,插入和维护最大值可以用堆来维护,看上去定点删除需要手写堆,但不难发现一条边仅会出现在割集中一次,因此只需要用一个标记数组标记它有没有被删掉过即可。那么我们就完成了此题,时间复杂度为O(mlogm)
    (这题好神……转化求最长路方法妙是妙,但也有迹可循,学习了)
    以下是本人代码:

    #include <bits/stdc++.h>
    using namespace std;
    int n,m,S,T,first[500010]={0},qfirst[500010]={0},tot=0;
    int in[500010]={0},val[500010][2]={0},q[500010];
    struct edge
    {
        int v,next;
    }e[2000010],qe[2000010];
    struct Pair
    {
        int id,val;
        bool operator < (Pair a) const
        {
            return val<a.val;
        }
    };
    bool inst[2000010]={0};
    priority_queue<Pair> Q;
    
    void insert(int a,int b)
    {
        e[++tot].v=b,e[tot].next=first[a],first[a]=tot;
        qe[tot].v=a,qe[tot].next=qfirst[b],qfirst[b]=tot;
    }
    
    void init()
    {
        scanf("%d%d",&n,&m);
        S=n+1,T=n+2;
        for(int i=1;i<=m;i++)
        {
            int a,b;
            scanf("%d%d",&a,&b);
            insert(a,b);
        }
        for(int i=1;i<=n;i++)
            insert(S,i),insert(i,T);
    }
    
    void pre()
    {
        int h,t;
    
        for(int i=1;i<=m;i++)
            in[e[i].v]++;
        h=1,t=0;
        for(int i=1;i<=n;i++)
            if (!in[i]) q[++t]=i,val[i][0]=0;
        while(h<=t)
        {
            int v=q[h++];
            for(int i=first[v];i;i=e[i].next)
            {
                in[e[i].v]--;
                val[e[i].v][0]=max(val[e[i].v][0],val[v][0]+1);
                if (!in[e[i].v]) q[++t]=e[i].v;
            }
        }
    
        for(int i=1;i<=m;i++)
            in[qe[i].v]++;
        h=1,t=0;
        for(int i=1;i<=n;i++)
            if (!in[i]) q[++t]=i,val[i][1]=0;
        while(h<=t)
        {
            int v=q[h++];
            for(int i=qfirst[v];i;i=qe[i].next)
            {
                in[qe[i].v]--;
                val[qe[i].v][1]=max(val[qe[i].v][1],val[v][1]+1);
                if (!in[qe[i].v]) q[++t]=qe[i].v;
            }
        }
    
        val[T][1]=-1;
    }
    
    void work()
    {
        int h,t;
    
        for(int i=1;i<=m;i++)
            in[e[i].v]++;
        h=1,t=0;
        for(int i=1;i<=n;i++)
            if (!in[i]) q[++t]=i,val[i][0]=0;
    
        Pair nxt;
        for(int i=first[S];i;i=e[i].next)
        {
            nxt.id=i;
            nxt.val=val[e[i].v][1];
            inst[i]=1;
            Q.push(nxt);
        }
    
        int ans=1000000000,ansi;
        while(h<=t)
        {
            int v=q[h++];
            for(int i=qfirst[v];i;i=qe[i].next)
                inst[i]=0;
    
            Pair x=Q.top();
            while(!inst[x.id]) Q.pop(),x=Q.top();
            if (ans>x.val)
            {
                ansi=v;
                ans=x.val;
            }
    
            for(int i=first[v];i;i=e[i].next)
            {
                nxt.id=i;
                nxt.val=val[v][0]+val[e[i].v][1]+1;
                inst[i]=1;
                Q.push(nxt);
                in[e[i].v]--;
                if (!in[e[i].v]) q[++t]=e[i].v;
            }
        }
    
        printf("%d %d",ansi,ans);
    }
    
    int main()
    {
        init();
        pre();
        work();
    
        return 0;
    }
  • 相关阅读:
    剑指 Offer 67. 把字符串转换成整数 && Leetcode 8 字符串转换整数 (atoi)
    剑指 Offer 49. 丑数 && Leetcode 264. 丑数 II
    [LeetCode] Implement strStr()
    [LeetCode] Rotate Image
    [LeetCode] Remove Duplicates from Sorted List II
    [LeetCode] Gas Station
    OpenCV-paper detection & perspective transformation 相关资料
    Install PIL on mac osX10.9
    为什么是 n(n+1)/2 ?
    数组排序
  • 原文地址:https://www.cnblogs.com/Maxwei-wzj/p/9793380.html
Copyright © 2020-2023  润新知