本人 6年级 耗时2个月luoguP2483 【模板】k 短路 / [SDOI2010] 魔法猪学院AC
接下来由我为大家讲解
对于原图以 𝑡t 为根建出任意一棵最短路径树 𝑇T,即反着从 𝑡t 跑出到所有点的最短路 𝑑𝑖𝑠dis
它有一些性质:
性质1:
对于一条 𝑠s 到 𝑡t 的路径的边集 𝑃P,去掉 𝑃P 中和 𝑇T 的交集,记为 𝑃′P′。
那么 𝑃′P′ 对于中任意相邻(从 𝑠s 到 𝑡t 的顺序)的两条边 𝑒,𝑓e,f,满足 𝑓f 的起点在 𝑇T 中为 𝑒e 的终点的祖先或者为相同点。
因为 𝑃P 中 𝑒,𝑓e,f 之间由树边相连或者直接相连。
性质2:
对于不在 𝑇T 中的边 𝑒e ,设 𝑢u 为起点,𝑣v 为终点,𝑤w为权值。
定义 Δ𝑒=𝑑𝑖𝑠𝑣+𝑤−𝑑𝑖𝑠𝑢Δe=disv+w−disu,即选这条边的路径和最短路的长度的差
设 𝐿𝑃LP 表示路径长度,则有
𝐿𝑃=𝑑𝑖𝑠𝑠+∑𝑒∈𝑝′Δ𝑒LP=diss+e∈p′∑Δe
这很显然。
性质3:
对于满足性质 11 的 𝑃′P′的定义的边集 𝑆S,有且仅有一条 𝑠s 到 𝑡t 的路径的边集 𝑃P,使得 𝑃′=𝑆P′=S。
因为树 𝑇T 上的两个点之间有且仅有一条路径。
问题转化
求第 𝑘k 小的满足性质 11 的 𝑃′P′的定义的边集
算法
用小根堆维护边集 𝑃P
初始 𝑃P 为空集(实际上只要维护边集当前尾部的边的起点是哪一个就好了,空集即 𝑠s)
每次取出最小权值的边集 𝑃P,设当前尾部的边的起点为 𝑥x
有两种方法可以得到一个新的边集:
**1.**替换 𝑥x 为起点的这条边为一条刚好大于等于它的非树边。
**2.**尾部接上一条起点为以 𝑥x 为起点的这条边的终点在 𝑇T 中祖先(包括自己)连出去的所有非树边的最小边。
然后就是怎么维护祖先出去的所有非树边的最小边:
显然可以从祖先转移过来,直接可并堆即可。
又因为要保留每个点的信息,所以合并的时候可持久化即可
和线段树合并的可持久化一样,然后就可以过了。
接下来送出AC代码 代码为C++14 运行
感谢大家的收看
#include<bits/stdc++.h>
#define maxn 5005
#define maxm 200005
#define eps 1e-8
using namespace std;
int n,m;
double E;
int info[maxn],Prev[maxm],to[maxm],cnt_e=1;
double cst[maxm];
void Node(int u,int v,double c){ Prev[++cnt_e]=info[u],info[u]=cnt_e,to[cnt_e]=v,cst[cnt_e]=c; }
bool usd[maxn];
int fa[maxn],faed[maxn];
double dis[maxn];
struct node{
int ch[2],to,dep;
double cst;
node(int to=0,double cst=0):to(to),cst(cst){dep=1,ch[0]=ch[1]=0;}
}t[maxm * 10];
int cnt,rt[maxn];
int cpy(int now){
t[++cnt] = t[now];
return cnt;
}
void merge(int &now,int l,int r){
if(!l || !r){ now = l+r; return; }
if(t[l].cst < t[r].cst) now = cpy(l) , merge(t[now].ch[1],t[l].ch[1],r);
else now = cpy(r) , merge(t[now].ch[1],l,t[r].ch[1]);
if(t[t[now].ch[1]].dep > t[t[now].ch[0]].dep) swap(t[now].ch[0],t[now].ch[1]);
t[now].dep = t[t[now].ch[1]].dep + 1;
}
void dfs(int now){
for(int i=info[now];i;i=Prev[i])
if(faed[to[i]] == i){
//printf("%d %d\n",to[i],now);
merge(rt[to[i]],rt[to[i]],rt[now]);
dfs(to[i]);
}
}
int main(){
scanf("%d%d%lf",&n,&m,&E);
for(int i=1,u,v;i<=m;i++){
double c;
scanf("%d%d%lf",&u,&v,&c);
Node(v,u,c);
}
priority_queue<pair<double,int>,vector<pair<double,int> >,greater<pair<double,int> > >q;
memset(dis,0x7f,sizeof dis);
dis[n] = 0 , q.push(make_pair(dis[n],n));
for(int now;!q.empty();){
now = q.top().second , q.pop();
if(usd[now]) continue;
usd[now] = 1;
for(int i=info[now];i;i=Prev[i])
if(dis[to[i]] > dis[now] + cst[i] + eps){
dis[to[i]] = dis[now] + cst[i] , fa[to[i]] = now;
faed[to[i]] = i;
q.push(make_pair(dis[to[i]],to[i]));
}
}
//for(int i=1;i<=n;i++) printf("$%d %.3lf\n",fa[i],dis[i]);
for(int i=1;i<=n;i++)
for(int j=info[i];j;j=Prev[j])
if(j != faed[to[j]] && to[j]!=n){
t[++cnt] = node(i,dis[i] + cst[j] - dis[to[j]]);
//printf("%d %d %.3lf\n",i,to[j],dis[i] + cst[j] - dis[to[j]]);
merge(rt[to[j]],rt[to[j]],cnt);
}
dfs(n);
int ans = 1;
E -= dis[1];
//printf("%d\n",rt[1]);
if(rt[1]) q.push(make_pair(dis[1]+t[rt[1]].cst,rt[1]));
for(int now;!q.empty();){
double val = q.top().first;
//printf("%.3lf %.3lf\n",val,E);
if(val > E + eps) break;
ans ++ , E -= val;
//printf("%.3lf\n",val);
now = q.top().second , q.pop();
for(int i=0;i<2;i++)
if(t[now].ch[i])
q.push(make_pair(val-t[now].cst+t[t[now].ch[i]].cst,t[now].ch[i]));
if(rt[t[now].to])
q.push(make_pair(val+t[rt[t[now].to]].cst,rt[t[now].to]));
}
printf("%d\n",ans);
}