题目描述

iPig在假期来到了传说中的魔法猪学院,开始为期两个月的魔法猪训练。经过了一周理论知识和一周基本魔法的学习之后,iPig对猪世界的世界本原有了很多的了解:众所周知,世界是由元素构成的;元素与元素之间可以互相转换;能量守恒……。 能量守恒……iPig 今天就在进行一个麻烦的测验。iPig 在之前的学习中已经知道了很多种元素,并学会了可以转化这些元素的魔法,每种魔法需要消耗 iPig 一定的能量。作为 PKU 的顶尖学猪,让 iPig 用最少的能量完成从一种元素转换到另一种元素……等等,iPig 的魔法导猪可没这么笨!这一次,他给 iPig 带来了很多 1 号元素的样本,要求 iPig 使用学习过的魔法将它们一个个转化为 N 号元素,为了增加难度,要求每份样本的转换过程都不相同。这个看似困难的任务实际上对 iPig 并没有挑战性,因为,他有坚实的后盾……现在的你呀! 注意,两个元素之间的转化可能有多种魔法,转化是单向的。转化的过程中,可以转化到一个元素(包括开始元素)多次,但是一但转化到目标元素,则一份样本的转化过程结束。iPig 的总能量是有限的,所以最多能够转换的样本数一定是一个有限数。具体请参看样例。

输入

第一行三个数 N、M、E 表示iPig知道的元素个数(元素从 1 到 N 编号)、iPig已经学会的魔法个数和iPig的总能量。 后跟 M 行每行三个数 si、ti、ei 表示 iPig 知道一种魔法,消耗 ei 的能量将元素 si 变换到元素 ti 。

输出

一行一个数,表示最多可以完成的方式数。输入数据保证至少可以完成一种方式。

样例输入

4 6 14.9
1 2 1.5
2 1 1.5
1 3 3
2 3 1.5
3 4 1.5
1 4 1.5

样例输出

3

提示

样例解释
有意义的转换方式共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}+\sum\limits_{e\in 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);
}

BZOJ1975[Sdoi2010]魔法猪学院——可持久化可并堆+最短路树的更多相关文章

  1. BZOJ1975 SDOI2010魔法猪学院(启发式搜索+最短路+堆)

    对反图跑最短路求出每个点到终点的最短路径,令其为估价函数大力A*,第k次到达某个点即是找到了到达该点的非严格第k短路,因为估价函数总是不大于实际值.bzoj可能需要手写堆.正解是可持久化可并堆,至今是 ...

  2. [BZOJ1975][SDOI2010]魔法猪学院(k短路,A*)

    1975: [Sdoi2010]魔法猪学院 Time Limit: 10 Sec  Memory Limit: 64 MBSubmit: 2748  Solved: 883[Submit][Statu ...

  3. bzoj1975: [Sdoi2010]魔法猪学院【k短路&A*算法】

    1975: [Sdoi2010]魔法猪学院 Time Limit: 10 Sec  Memory Limit: 64 MBSubmit: 2446  Solved: 770[Submit][Statu ...

  4. BZOJ1975 [Sdoi2010]魔法猪学院

    本文版权归ljh2000和博客园共有,欢迎转载,但须保留此声明,并给出原文链接,谢谢合作. 本文作者:ljh2000 作者博客:http://www.cnblogs.com/ljh2000-jump/ ...

  5. 洛谷P2483 Bzoj1975 [SDOI2010]魔法猪学院

    题目描述 iPig在假期来到了传说中的魔法猪学院,开始为期两个月的魔法猪训练.经过了一周理论知识和一周基本魔法的学习之后,iPig对猪世界的世界本原有了很多的了解:众所周知,世界是由元素构成的:元素与 ...

  6. BZOJ1975 [Sdoi2010]魔法猪学院 k短路

    欢迎访问~原文出处——博客园-zhouzhendong 去博客园看该题解 题目传送门 - BZOJ1975 题意概括 给出一个无向图,让你走不同的路径,从1到n,路径长度之和不超过E,求最大路径条数. ...

  7. 【k短路&A*算法】BZOJ1975: [Sdoi2010]魔法猪学院

    Description 找出1~k短路的长度.   Solution k短路的求解要用到A*算法 A*算法的启发式函数f(n)=g(n)+h(n) g(n)是状态空间中搜索到n所花的实际代价 h(n) ...

  8. BZOJ1975 SDOI2010魔法猪学院

    就是个A*,具体原理可以参考VANE的博文. 正解要手写堆,会被卡常,也许哪天我筋搭错了写一回吧. #include<bits/stdc++.h> #define r register u ...

  9. 【BZOJ1975】[Sdoi2010]魔法猪学院 A*

    [BZOJ1975][Sdoi2010]魔法猪学院 Description iPig在假期来到了传说中的魔法猪学院,开始为期两个月的魔法猪训练.经过了一周理论知识和一周基本魔法的学习之后,iPig对猪 ...

随机推荐

  1. 广州.NET微软技术俱乐部 微信群有用信息集锦

    考虑到广州.NET微软技术俱乐部 微信群 十分活跃. 有用信息很有可能被淹没. 所以建立此贴. 首先群的活跃是十分重要的. 所以我是不可能把群搞得像技术论坛和github一样, 因为微信群的定位我在& ...

  2. Jmeter设置代理,抓包之app请求

    步骤: 1. Jmeter选择测试计划,添加线程组,添加http请求,添加监听器-察看结果树 2. 添加http代理服务器,右键添加非测试元件-添加http代理服务器 3. 端口改为8889,目标控制 ...

  3. ASP.NET Core 入门教程 7、ASP.NET Core MVC 分部视图入门

    一.前言 1.本教程主要内容 ASP.NET Core MVC (Razor)分部视图简介 ASP.NET Core MVC (Razor)分部视图基础教程 ASP.NET Core MVC (Raz ...

  4. MySQL5.5.51启用网络远程连接

    在其它电脑主机上访问时提示host ip is not allowed to connect to this mysql 下面代码为解决该问题的方法: :\Program Files\mysql-\b ...

  5. C 存储类

    存储类定义 C 程序中变量/函数的范围(可见性)和生命周期.这些说明符放置在它们所修饰的类型之前.下面列出 C 程序中可用的存储类: auto.register.static.extern auto ...

  6. kaptcha验证码的使用

    使用kaptcha可以方便的配置: 验证码的字体 验证码字体的大小 验证码字体的字体颜色 验证码内容的范围(数字,字母,中文汉字!) 验证码图片的大小,边框,边框粗细,边框颜色 验证码的干扰线(可以自 ...

  7. 日志学习系列(三)——NLog基础知识

    前边我们解释了log4net的学习,我们再介绍一下NLog 一.什么是NLog NLog是一个基于.NET平台编写的类库,我们可以使用NLog在应用程序中添加极为完善的跟踪调试代码.NLog是一个简单 ...

  8. C#动态调用webService出现 基础连接已经关闭: 未能为 SSL/TLS 安全通道建立信任关系。

    这里因为的原因是https请求要检查证书,有些证书不正确的,网页不会正常展示内容,而会返回链接不安全,是否继续.不安全的链接是否继续. 详情参考: C#动态调用webService出现 基础连接已经关 ...

  9. LoadRunner 11 error:Cannot initialize driver dll

    LoadRunner 11 error:Cannot initialize driver dll 这个错误很容易解决,使用win7系统时,有些程序要以管理员身份才能运行. 解决方案:右键选择:“以管理 ...

  10. linux用户身份和文件权限

    1.用户身份与能力 root管理员是linux 的超级用户,他拥有系统的所有权,能够管理系统的各项功能,如添加/删除用户,启动/关闭服务进程,开启/禁用硬件设备…… "Linux系统中的管理 ...