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;
}

浙公网安备 33010602011771号