题目描述
输入
输出
样例输入
1 2 1.5
2 1 1.5
1 3 3
2 3 1.5
3 4 1.5
1 4 1.5
样例输出
提示
样例解释
有意义的转换方式共4种:
1->4,消耗能量 1.5
1->2->1->4,消耗能量 4.5
1->3->4,消耗能量 4.5
1->2->3->4,消耗能量 4.5
显然最多只能完成其中的3种转换方式(选第一种方式,后三种方式仍选两个),即最多可以转换3份样本。
如果将 E=14.9 改为 E=15,则可以完成以上全部方式,答案变为 4。
数据规模
占总分不小于 10% 的数据满足 N <= 6,M<=15。
占总分不小于 20% 的数据满足 N <= 100,M<=300,E<=100且E和所有的ei均为整数(可以直接作为整型数字读入)。
所有数据满足 2 <= N <= 5000,1 <= M <= 200000,1<=E<=107,1<=ei<=E,E和所有的ei为实数。
首先对反向边跑最短路建出以$n$为根的最短路树。
对于一条从$1$到$n$的路径上的边集$S$(边有顺序),除去在最短路上的边,剩下的边组成的集合为$S'$(按$S$中的顺序),那么对于$S'$中顺序相邻的两条边$(u,v)$和$(s,t)$,$s$一定是$v$的祖先或相同点(因为$s$与$v$在树上直接相连或由树边相连)。
我们设$val_{e}=d_{v}+w-d_{u}$,其中$e$为一条不在最短路树上的边,$d_{i}$表示点$i$到$n$的最短路长度,$w$为这条边的边权,$u,v$分别为这条边的起点和终点。
那么一条从$1$到$n$的路径长度$len$就可以表示成$len=d_{1}+sumlimits_{ein S'}^{ }val_{e}$。
那么问题就转化成求第$k$小的$S'$。
最小的$S'$显然是空集,即$S$为从$1$到$n$的最短路。
那么现在考虑如何获得一个新的相对较小的边集,对于一个边集$S'$有两种方法(假设$S'$中最后一条边为$(u,v)$):
1、将$(u,v)$换成以$u$或$u$的祖先为起点的$val$最小的一条非树边。
2、在$S'$的最后添加一条新的非树边满足这条非树边的起点是$v$在最短路树上的祖先或$v$本身,当然也是使这条边的$val$尽量小。
那么我们只需要维护一个小根堆,每次取出堆顶的边集并将通过这个边集获得的新的边集加入堆中即可。
对于两种获得新边集的方法,都需要维护出这个点到$n$路径上所有点的堆中的信息。
我们将每个堆变为可并堆并可持久化,即每个点的堆保存了从这个点到根路径上所有点的堆的信息。
将每个点的可并堆像线段树合并的可持久化一样与子节点的堆合并即可。
$bzoj$与$luogu$的精度不同,代码附上两个版本的。
$bzoj$
#include<set> #include<map> #include<queue> #include<stack> #include<cmath> #include<vector> #include<bitset> #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> #define ll long long #define pr pair<double,int> const double eps = 1e-6; using namespace std; int tot; int cnt; int head[5010]; int to[400010]; int next[400010]; double val[400010]; int root[5010]; int ls[2000010]; int rs[2000010]; int end[2000010]; double v[2000010]; double d[5010]; int dis[2000010]; int n,m; double E,z; int x,y; int vis[5010]; int from[5010]; int f[400010]; vector<int>e[5010]; int ans; priority_queue< pr,vector<pr>,greater<pr> >q; void add(int x,int y,double z) { next[++tot]=head[x]; head[x]=tot; to[tot]=y; val[tot]=z; } int build(double val,int to) { int rt=++cnt; v[rt]=val; end[rt]=to; dis[rt]=1; return rt; } int merge(int x,int y) { if(!x||!y) { return x+y; } if(v[x]-v[y]>=eps) { swap(x,y); } int rt=++cnt; ls[rt]=ls[x],rs[rt]=rs[x],end[rt]=end[x],dis[rt]=dis[x],v[rt]=v[x]; rs[rt]=merge(rs[rt],y); if(dis[ls[rt]]<dis[rs[rt]]) { swap(ls[rt],rs[rt]); } dis[rt]=dis[rs[rt]]+1; return rt; } void dfs(int x) { int size=e[x].size(); for(int i=0;i<size;i++) { int to=e[x][i]; root[to]=merge(root[to],root[x]); dfs(to); } } int main() { scanf("%d%d%lf",&n,&m,&E); for(int i=1;i<=m;i++) { scanf("%d%d%lf",&x,&y,&z); add(x,y,z); add(y,x,z); } memset(d,127,sizeof(d)); d[n]=0; q.push(make_pair(d[n],n)); while(!q.empty()) { int now=q.top().second; q.pop(); if(vis[now]) { continue; } vis[now]=1; for(int i=head[now];i;i=next[i]) { if(i&1) { continue; } if(d[to[i]]>d[now]+val[i]) { from[to[i]]=i-1; d[to[i]]=d[now]+val[i]; q.push(make_pair(d[to[i]],to[i])); } } } for(int i=1;i<n;i++) { f[from[i]]=1; e[to[from[i]]].push_back(i); } for(int i=1;i<=tot;i+=2) { if(!f[i]) { root[to[i+1]]=merge(root[to[i+1]],build(val[i]+d[to[i]]-d[to[i+1]],to[i])); } } dfs(n); if(E-d[1]>=eps) { E-=d[1]; ans++; } if(root[1]) { q.push(make_pair(v[root[1]],root[1])); } while(!q.empty()) { int now=q.top().second; double res=q.top().first; if(E-d[1]-res<eps) { break; } q.pop(); E-=d[1]+res; ans++; if(ls[now]) { q.push(make_pair(v[ls[now]]+res-v[now],ls[now])); } if(rs[now]) { q.push(make_pair(v[rs[now]]+res-v[now],rs[now])); } if(root[end[now]]) { q.push(make_pair(res+v[root[end[now]]],root[end[now]])); } } printf("%d",ans); }
$luogu$
#include<set> #include<map> #include<queue> #include<stack> #include<cmath> #include<vector> #include<bitset> #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> #define ll long long #define pr pair<long double,int> const long double eps = 1e-8; using namespace std; int tot; int cnt; int head[5010]; int to[400010]; int next[400010]; long double val[400010]; int root[5010]; int ls[4000010]; int rs[4000010]; int end[4000010]; long double v[4000010]; long double d[5010]; int dis[4000010]; int n,m; long double E,z; int x,y; int vis[5010]; int from[5010]; int f[400010]; vector<int>e[5010]; int ans; priority_queue< pr,vector<pr>,greater<pr> >q; void add(int x,int y,long double z) { next[++tot]=head[x]; head[x]=tot; to[tot]=y; val[tot]=z; } int build(long double val,int to) { int rt=++cnt; v[rt]=val; end[rt]=to; dis[rt]=1; return rt; } int merge(int x,int y) { if(!x||!y) { return x+y; } if(v[x]-v[y]>=eps) { swap(x,y); } int rt=++cnt; ls[rt]=ls[x],rs[rt]=rs[x],end[rt]=end[x],dis[rt]=dis[x],v[rt]=v[x]; rs[rt]=merge(rs[rt],y); if(dis[ls[rt]]<dis[rs[rt]]) { swap(ls[rt],rs[rt]); } dis[rt]=dis[rs[rt]]+1; return rt; } void dfs(int x) { int size=e[x].size(); for(int i=0;i<size;i++) { int to=e[x][i]; root[to]=merge(root[to],root[x]); dfs(to); } } int main() { scanf("%d%d%Lf",&n,&m,&E); for(int i=1;i<=m;i++) { scanf("%d%d%Lf",&x,&y,&z); add(x,y,z); add(y,x,z); } for(int i=1;i<=n;i++) { d[i]=(long double)50000000000001.0; } d[n]=0; q.push(make_pair(d[n],n)); while(!q.empty()) { int now=q.top().second; q.pop(); if(vis[now]) { continue; } vis[now]=1; for(int i=head[now];i;i=next[i]) { if(i&1) { continue; } if(d[to[i]]+eps>d[now]+val[i]) { from[to[i]]=i-1; d[to[i]]=d[now]+val[i]; q.push(make_pair(d[to[i]],to[i])); } } } for(int i=1;i<n;i++) { f[from[i]]=1; e[to[from[i]]].push_back(i); } for(int i=1;i<=tot;i+=2) { if(!f[i]) { root[to[i+1]]=merge(root[to[i+1]],build(val[i]+d[to[i]]-d[to[i+1]],to[i])); } } dfs(n); if(E-d[1]>=eps) { E-=d[1]; ans++; } if(root[1]) { q.push(make_pair(v[root[1]],root[1])); } while(!q.empty()) { int now=q.top().second; long double res=q.top().first; if(E-d[1]-res<-eps) { break; } q.pop(); E-=d[1]+res; ans++; if(ls[now]) { q.push(make_pair(v[ls[now]]+res-v[now],ls[now])); } if(rs[now]) { q.push(make_pair(v[rs[now]]+res-v[now],rs[now])); } if(root[end[now]]) { q.push(make_pair(res+v[root[end[now]]],root[end[now]])); } } printf("%d",ans); }