Task 3. 雨中冒险
( rain.cpp/c/pas)
【 问题描述】
有 n 个节点,标号为 1..n。 m 条双向公路连接着这些节点,其中第 i 条公路
连接着 u_i 和 v_i,从一端走到另一端需要 w_i 秒。现在,小 Y 打算从学校回到
家里。
学校是节点 1,小 Y 家是节点 n,保证存在至少一条从节点 1 到节点 n 的路
径。
在第 0 秒,小 Y 身处节点 1,他的目标是尽早到达节点 n。根据天气预报,
接下来会有 k 次暴雨,第 i 次暴雨的时间为第 l_i 秒至第 r_i 秒,保证每次暴雨
的时间段互不重叠(包括起止时间)。由于小 Y 忘了带伞,因此在下雨期间,他
只能躲在某个节点里面避雨。如果某一个时刻在下暴雨,而小 Y 还在某条公路上,
那么他会被淋惨。
为了帮助小 Y 尽快回到家,上帝决定实现小 Y 一个愿望。小 Y 可以指定某一
条道路,然后这条道路两端的节点将合并成一个节点(合并出的节点拥有原来两
个节点的所有出边)。
请你帮小 Y 计算,在不被淋惨的前提下最早第多少秒他能回到自己家中?
注:对于一场第 l..r 秒的暴雨,若小 Y 第 r 秒从节点出发,或者第 l 秒到
达某个节点,那么他是不会淋到雨的。
【输入】
输入文件名: rain.in
第一行三个数 n、 m、 k。
接下来 m 行,第 i 行三个整数表示 u_i、 v_i、 w_i。
接下来 k 行,第 i 行两个整数表示 l_i、 r_i。
全国信息学奥林匹克联赛( NOIP2016)模拟赛 提高组 day1
【输出】
输出文件名: rain.out
第一行一个整数,表示小 Y 最早第多少秒能回到家中。
【数据范围】
测试点 1..6: k = 0
测试点 7..12: n、 m、 k≤10^3
测试点 1..20: 2≤n≤10^5, 1≤m≤2*10^5, 0≤k≤10^5, 1≤w_i≤10^4,
0≤l_i<r_i≤109,且保证输入的暴雨时间段互不相交、 l_i 严格递增。
这是一道挺不错的最短路题;
对于第一个部分分,对于每个点拆成两个点,分别表示用了缩边和没用缩边两种状态,相当于跑一个分层图最短路;
对于第二个部分分,首先仍是拆点,考虑如何处理下雨的情况:
比如从x到y距离为w,现在到x的最短路为dis;
于是相当于在dis后找到一段离dis最近的且长度为w的区间使其不被下雨天覆盖;
对于第二个部分分,可以暴力一场一场地等雨停然后判断从天晴后跑能否在下雨前跑到;
对于第三个部分分,我们要充分挖掘下雨的性质:
能走的时间是下雨间的间隙,对于第i场雨天晴后可走的时间是l[i+1]-r[i],记为rest[i];
那么上面那个问题转化为在dis后找到第一个一个大于等于w的rest[i],然后从r[i]开始走即可(特判一下不用等雨直接走的情况)
于是可以通过ST表+倍增求出找到第一个满足条件的雨后,然后开始转移即可(ST[j][i]表示从i走2^j场雨的最大值)
#include<iostream> #include<vector> #include<queue> #include<algorithm> #include<cstring> #include<cstdio> #include<cmath> using namespace std; int gi() { int x=0,flag=1; char ch=getchar(); while(ch<‘0‘||ch>‘9‘){if(ch==‘-‘) flag=-1;ch=getchar();} while(ch>=‘0‘&&ch<=‘9‘) x=x*10+ch-‘0‘,ch=getchar(); return x*flag; } const int N=200010; const int M=1200010; const int Inf=2147483647; const int K=20; int cnt,head[N],nxt[M],to[M],val[M],dis[N]; void lnk(int x,int y,int z){ to[++cnt]=y,nxt[cnt]=head[x],val[cnt]=z,head[x]=cnt; } int l[N],r[N],ST[K][N],pre[K]; struct data{ int x,d; bool operator <(const data b) const{return d>b.d;} }; priority_queue <data> q; bool vis[N]; int main(){ freopen("rain.in","r",stdin); freopen("rain.out","w",stdout); pre[0]=1;for(int i=1;i<K;i++)pre[i]=pre[i-1]<<1; int n=gi(),m=gi(),k=gi(); for(int i=1;i<=m;i++){ int u=gi(),v=gi(),w=gi(); lnk(u,v,w);lnk(v,u,w); lnk(u,n+v,0);lnk(v,n+u,0); lnk(n+u,n+v,w);lnk(n+v,n+u,w); } for(int i=1;i<=k;i++)l[i]=gi(),r[i]=gi(); for(int i=1;i<k;i++)ST[0][i]=l[i+1]-r[i];ST[0][k]=10000; for(int j=1;j<K;j++) for(int i=1;i<=k;i++) ST[j][i]=max(ST[j-1][i],ST[j-1][i+pre[j-1]]); for(int i=1;i<=n;i++)dis[i]=dis[n+i]=Inf; dis[1]=0; q.push((data){1,0}); while(!q.empty()){ int x=q.top().x;q.pop(); if(vis[x])continue;vis[x]=1; int last=upper_bound(r+1,r+k+1,dis[x])-r-1; for(int i=head[x];i;i=nxt[i]){ int y=to[i]; if(last==k||l[last+1]-dis[x]>=val[i]){ if(dis[y]>dis[x]+val[i]) q.push((data){y,dis[y]=dis[x]+val[i]}); } else{ int nex=last+1; for(int j=K-1;j>=0;j--) if(ST[j][nex]<val[i])nex+=pre[j]; if(dis[y]>r[nex]+val[i]) q.push((data){y,dis[y]=r[nex]+val[i]}); } } } printf("%d",min(dis[n],dis[n+n])); return 0; }