P2483 【模板】k 短路 / [SDOI2010] 魔法猪学院

题意

给出一张 \(n\) 个点,\(m\) 条边的有向带权图,求出最大的 \(k\) 满足前 \(k\) 短路径加长度起来大于 \(E\)
\(n\le5010\)\(m\le2\times10^5\)\(E\le10^7\),边权 \(w\le E\)

思路

先用 \(dij\) 在反图上跑,建一个以 \(n\) 为根的最短路树,对于一个点 \(u\),定义 \(d(u)\)\(u\)\(n\) 的最短路长度。对于一条边 \((u,v,w)\),定义祂的新权值 \(w'=w+d(v)-d(u)\)。对于一条路径,把路径上的非最短路树边按照顺序领出来,设为 \((u_1,v_1),(u_2,v_2),\dots\),那么对于每个 \(i\),有 \(u_{i+1}\)\(v_i\) 在最短路树上的祖先,而这条路径的边权和为 \(d(s)\) 加所有非最短路树边的新权值和。
现在来考虑一个暴力:在每个点 \(u\) 上存所有以 \(u\) 的某个祖先为起点的非最短路树边,用一个优先队列存现在有哪些路经,每次选最短的,取出来,并把祂能到直接达的所有点放进优先队列。
显然,这样是过不了的,但是可以用可持久化可并堆优化。具体的,每个点开可并小根堆,存以这个点祖先为起点的所有非最短路树边。显然,同一个堆中只有堆顶需要放进优先队列。在把一个路径取出来时,只需要把祂在堆上的左右儿子和祂存的边能到达的点加入优先队列。

代码

// Problem: P2483 【模板】k 短路 / [SDOI2010] 魔法猪学院
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P2483
// Memory Limit: 128 MB
// Time Limit: 1000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

#include<bits/stdc++.h>
using namespace std;
namespace IO{
    template<typename T>
    inline void read(T&x){
        x=0;char c=getchar();bool f=0;
        while(!isdigit(c)) c=='-'?f=1:0,c=getchar();
        while(isdigit(c)) x=x*10+c-'0',c=getchar();
        f?x=-x:0;
    }
    template<typename T>
    inline void write(T x){
        if(x==0){putchar('0');return ;}
        x<0?x=-x,putchar('-'):0;short st[50],top=0;
        while(x) st[++top]=x%10,x/=10;
        while(top) putchar(st[top--]+'0');
    }
    inline void read(char&c){c=getchar();while(isspace(c)) c=getchar();}
    inline void write(char c){putchar(c);}
    inline void read(double&x){scanf("%lf",&x);}
    inline void write(double x){printf("%lf",x);}
    inline void read(string&s){s.clear();char c;read(c);while(!isspace(c)&&~c) s+=c,c=getchar();}
    inline void write(string s){for(int i=0,len=s.size();i<len;i++) putchar(s[i]);}
    template<typename T>inline void write(T*x){while(*x) putchar(*(x++));}
    template<typename T,typename...T2> inline void read(T&x,T2&...y){read(x),read(y...);}
    template<typename T,typename...T2> inline void write(const T x,const T2...y){write(x),putchar(' '),write(y...),sizeof...(y)==1?putchar('\n'):0;}
}using namespace IO;
const int maxn=5010,maxm=200010;
const double eps=1e-8;
int n,m,fa[maxn],rt[maxn];
double E,dis[maxn];
struct EDGE{int u,v;double w;}b[maxm];
vector<pair<int,double>>e[maxn],ef[maxn];
vector<int>tr[maxn];
bool flag[maxn],in[maxn];
namespace Leftist_Tree{
    struct node{int ch[2],dist,to;double val;}t[maxm*20];
    int cnt;
    int merge(int u,int v){
        if(!u||!v) return u+v;
        if(t[u].val>t[v].val) swap(u,v);
        int clone=++cnt;
        t[clone]=t[u];
        t[clone].ch[1]=merge(t[u].ch[1],v);
        if(t[t[clone].ch[0]].dist<t[t[clone].ch[1]].dist) swap(t[clone].ch[0],t[clone].ch[1]);
        t[clone].dist=t[t[clone].ch[1]].dist+1;
        return clone;
    }
};
void dij(int s){
    priority_queue<pair<double,int>>q;
    for(int i=1;i<=n;i++) dis[i]=1e15;
    dis[s]=0;
    q.push({0,s});
    while(!q.empty()){
        auto[l,u]=q.top();q.pop();
        if(flag[u]) continue;flag[u]=1;
        for(auto[v,w]:ef[u]) if(dis[v]>dis[u]+w) dis[v]=dis[u]+w,q.push({-dis[v],v});
    }
}
void dfs(int u){
    for(int v:tr[u]){
        rt[v]=Leftist_Tree::merge(rt[u],rt[v]);
        dfs(v);
    }
}
priority_queue<pair<double,int>,vector<pair<double,int>>,greater<pair<double,int>>>qu;
signed main(){
    read(n,m,E);
    for(int i=1;i<=m;i++){
        int u,v;double w;read(u,v,w);
        if(u==n) w=1e15;
        e[u].push_back({v,w}),ef[v].push_back({u,w});
        b[i]={u,v,w};
    }
    dij(n);
    for(int i=1;i<=n;i++) for(auto[v,w]:ef[i]) if(abs(dis[v]-dis[i]-w)<eps) fa[v]=i;
    for(int i=1;i<=n;i++) if(dis[i]<=1e14) in[i]=1;
    for(int i=1;i<=m;i++){
        auto[u,v,w]=b[i];
        if((fa[u]==v&&abs(dis[u]-(dis[v]+w))<eps)) continue;
        w=w+dis[v]-dis[u];
        if(in[u]&&in[v]){
            int d;
            Leftist_Tree::t[d=++Leftist_Tree::cnt]={{0,0},0,v,w};
            if(rt[u]==0) rt[u]=d;
            else rt[u]=Leftist_Tree::merge(rt[u],d);
        }
    }
    for(int i=1;i<=n;i++) if(in[i]&&fa[i]) tr[fa[i]].push_back(i);
    dfs(n);
    int ans=1;
    E-=dis[1];
    qu.push({dis[1]+Leftist_Tree::t[rt[1]].val,rt[1]});
    while(!qu.empty()){
        auto[l,u]=qu.top();qu.pop();
        E-=l;
        if(E<0) break;
        ans++;
        if(Leftist_Tree::t[u].ch[0]) qu.push({l-Leftist_Tree::t[u].val+Leftist_Tree::t[Leftist_Tree::t[u].ch[0]].val,Leftist_Tree::t[u].ch[0]});
        if(Leftist_Tree::t[u].ch[1]) qu.push({l-Leftist_Tree::t[u].val+Leftist_Tree::t[Leftist_Tree::t[u].ch[1]].val,Leftist_Tree::t[u].ch[1]});
        int d=rt[Leftist_Tree::t[u].to];
        if(d) qu.push({l+Leftist_Tree::t[d].val,d});
    }
    write(ans);
    return 0;
}
posted @ 2026-07-08 20:35  Link-Cut_Trees  阅读(11)  评论(0)    收藏  举报