【bzoj 3832】Rally(拓扑排序+Heap)

传送门biu~
如果不删点的话,那就是很简单的一个dp。因为起终点不能确定,所以建一个超级源S和一个超级汇T。问题转化为求从S到T的最长路。
对于删点的问题,我们可以预处理S到每个点的最长路,和每个点到T的最长路,这样就可以给边赋上权值来代表经过这条边的最长路。将图按拓扑序分层,按拓扑序枚举每一个点。将这一个点连接的“上一层”的边删除,再将“下一层”的边加入,两个过程中间的状态就恰好是”这个点不在“的状态,这个时候记录最长路为答案。显然,如果加入的“下一层”的边正好是下一个点“上一层”的边,这条边也会被删除,也就是说不会有多条边同时代表同一条最长路的情况。
对于维护最值可以用带删除指定元素的Heap实现,也可以用权值线段树实现。这里写上Heap版:

#include<bits/stdc++.h>
using namespace std;
struct Heap{
    priority_queue<int>a,b;
    inline void update(){while(!a.empty() && !b.empty() && a.top()==b.top())a.pop(),b.pop();}
    inline void del(int x){b.push(x);update();}
    inline int top(){return a.top();}
    inline void push(int x){a.push(x);update();}
}q;
int head[500005],fir[500005],ru[500005],nex[2000005],to[2000005],tp;
inline void add(int x,int y){
    ++ru[y];
    nex[++tp]=head[x];
    head[x]=tp;
    to[tp]=y;
    nex[++tp]=fir[y];
    fir[y]=tp;
    to[tp]=x;
}
int n,m,ans,len=1e9;
int topo[500005],S[500005],T[500005];
void init() {
    queue<int>q;int cnt=0;
    for(int i=1;i<=n;i++)
        if(!ru[i]) q.push(i);
    while(!q.empty()){
        int x=q.front();q.pop();topo[++cnt]=x;
        for(int i=head[x];i;i=nex[i])
            if(!--ru[to[i]])   q.push(to[i]);
    }
    for(int j=1;j<=n;j++){
        int x=topo[j];
        S[x]=max(1,S[x]);
        for(int i=head[x];i;i=nex[i]) S[to[i]]=max(S[to[i]],S[x]+1);
    }
    for(int j=n;j>=1;j--){
        int x=topo[j];
        T[x]=max(1,T[x]);
        for(int i=head[x];i;i=nex[i]) T[x]=max(T[x],T[to[i]]+1);
    }
}
int main(){
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++) {
        int u,v;
        scanf("%d%d",&u,&v);
        add(u,v);
    }
    init();
    q.push(0);
    for(int i=1;i<=n;i++) q.push(T[i]);
    for(int j=1;j<=n;j++){
        int x=topo[j];
        for(int i=fir[x];i;i=nex[i])    q.del(S[to[i]]+T[x]);
        q.del(T[x]);
        if(q.top()<len)     len=q.top(),ans=x;
        for(int i=head[x];i;i=nex[i])   q.push(S[x]+T[to[i]]);
        q.push(S[x]);
    }
    printf("%d %d\n",ans,len-1);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

zP1nG

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值