BZOJ3832[Poi2014] Rally

30 篇文章 0 订阅
17 篇文章 0 订阅

Task:给定一个有向无环图,边的长度都为1。找到一个点使得删掉这个点后图中剩余的最长路径最短。(N<=500 000,M<=1 000 000)

Solution:

神一样的题…

将原图划分为两个集合 S,T 。一开始所有点都属于 T ,然后按照拓扑序逐步将T中的点移到 S 中,同时统计答案。

当然这样讲并不能懂,来看一看实际的做法:

  • 拓扑dp处理出,表示到点的最长路,点出发的最长路。
  • 首先把所有的都加入到堆中(所有点属于T

    按照拓扑序删点:

    • 对于一个点 i
    • 删除g[i]
    • 删除 f[j]+g[i]+1(ji) 【入边】
    • 统计答案【取堆顶】
    • 加入 f[i]+g[j]+1(ij) 【出边】
    • 加入 f[i]

    的过程中其实就是将拓扑序中前一个点的状态转移到当前点上的一个过程。

    什么?你说堆不能删除?—来自某大神

    #include<ctime>
    #include<cmath>
    #include<cstdio>
    #include<cctype>
    #include<cstdlib>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    #include<set>
    #include<map>
    #include<queue>
    #include<vector>
    #include<string>
    #define fi first
    #define se second
    #define ll long long
    #define ull unsigned long long
    #define lson (p<<1)
    #define rson (p<<1|1)
    #define mp make_pair
    #define pb push_back
    #define pll pair<ll,ll>
    #define pii pair<int,int>
    #define lowbit(x) ((x)&(-x))
    #define siz(x) (int)((x).size())
    using namespace std;
    inline void Rd(int &res){
        char c;res=0;
        while(c=getchar(),c<'0');
        do{
            res=(res<<1)+(res<<3)+(c^48);
        }while(c=getchar(),c>='0');
    }
    const int M=500005;
    struct Edge{int to,nxt;}Edge[M<<1],rEdge[M<<1];
    int Head[M],rHead[M],tot,degree[M],rdegree[M],degree2[M],n;
    inline void Addedge(int a,int b){
        Edge[++tot].to=b;Edge[tot].nxt=Head[a];Head[a]=tot;degree[b]++;degree2[b]++;
        rEdge[tot].to=a;rEdge[tot].nxt=rHead[b];rHead[b]=tot;rdegree[a]++;
    }
    int Q[M],f[M],g[M];
    struct Heap{//可删除堆
        priority_queue<int>q,p;
        void Push(int x){
            q.push(x);
        }
        void Del(int x){
            p.push(x);
            while(!q.empty()&&!p.empty()&&q.top()==p.top())q.pop(),p.pop();
        }
        int Top(){
            if(q.empty())return 0;
            else return q.top();
        }
    }Heap;
    void Topo_pre(){
        int L=0,R=-1;
        for(int i=1;i<=n;i++)
            if(degree[i]==0)Q[++R]=i;
        while(L<=R){
            int now=Q[L++];
            for(int i=Head[now];~i;i=Edge[i].nxt){
                int to=Edge[i].to;
                degree[to]--;
                if(f[now]+1>f[to])f[to]=f[now]+1;
                if(degree[to]==0)Q[++R]=to;
            }
        }
        L=0,R=-1;
        for(int i=1;i<=n;i++)
            if(rdegree[i]==0)Q[++R]=i;
        while(L<=R){
            int now=Q[L++];
            for(int i=rHead[now];~i;i=rEdge[i].nxt){
                int to=rEdge[i].to;
                rdegree[to]--;
                if(g[now]+1>g[to])g[to]=g[now]+1;
                if(rdegree[to]==0)Q[++R]=to;
            }
        }
    }
    void Topo_solve(){
        int L=0,R=-1,ans=n*2,id;
        for(int i=1;i<=n;i++)
            if(degree2[i]==0)Q[++R]=i;
        while(L<=R){
            int now=Q[L++];
            Heap.Del(g[now]);
            for(int j=rHead[now];~j;j=rEdge[j].nxt){
                int to=rEdge[j].to;
                Heap.Del(f[to]+g[now]+1);
            }
            if(Heap.Top()<ans){
                ans=Heap.Top();
                id=now;
            }
            for(int j=Head[now];~j;j=Edge[j].nxt){
                int to=Edge[j].to;
                Heap.Push(f[now]+g[to]+1);
                degree2[to]--;
                if(degree2[to]==0)Q[++R]=to;
            }
            Heap.Push(f[now]);
        }
        printf("%d %d\n",id,ans);
    }
    int main(){
        memset(Head,-1,sizeof(Head));
        memset(rHead,-1,sizeof(rHead));
        int m;
        Rd(n),Rd(m);
        while(m--){
            int a,b;
            Rd(a),Rd(b);
            Addedge(a,b);
        }
        Topo_pre();
        for(int i=1;i<=n;i++)Heap.Push(g[i]);
        Topo_solve();
        return 0;
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值