雨中冒险
Posted Z-Y-Y-S
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了雨中冒险相关的知识,希望对你有一定的参考价值。
【问题描述】
有 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≤10 9 ,且保证输入的暴雨时间段互不相交、l_i 严格递增。
首先,建立分层图:
dist[i][0/1]表示i点,0表示未缩点,1表示已缩
现在唯一的问题就是怎麽算出到达时间
at[i][0/1]表示i点0/1状态的上一场雨的编号,令为l
要幺直接走
或者对于每一场雨的间隔走w,走最靠前的大于w的边权
这个怎麽求?
用倍增,存间隔长的最大值
此题奇鬼无比,还要用set优化,玄学
#include<iostream> #include<cstdio> #include<algorithm> #include<cstring> #include<queue> #include<set> using namespace std; typedef long long lol; typedef pair<int,int> zt; struct Node { int next,to; int dis; } edge[400005]; int num,head[100005],n,m,k; int dist[100005][2],L[100005],R[100005],st[20][100005],p[100005]; int at[100005][2]; bool vis[100005][2]; lol gi() { lol x=0; char ch=getchar(); while (ch<‘0‘||ch>‘9‘) ch=getchar(); while (ch>=‘0‘&&ch<=‘9‘) { x=x*10+ch-‘0‘; ch=getchar(); } return x; } void add(int u,int v,int d) { num++; edge[num].next=head[u]; head[u]=num; edge[num].to=v; edge[num].dis=d; } int count(int x,int w,int &np,int as) { int i,l; l=as+1; x=max(R[as],x); if (L[as+1]-x>=w) { np=as; return x+w; } for (i=17; i>=0; i--) { if (l+(1<<i)<=k&&st[i][l]<w) { l+=(1<<i); } } np=l; return R[l]+w; } void SPFA() { memset(dist,127/3,sizeof(dist)); set<zt>Q; Q.insert((zt){1,0}); dist[1][0]=0; at[1][0]=0; while (Q.size()) { zt u=*Q.begin(); Q.erase(Q.begin()); vis[u.first][u.second]=0; for (int i=head[u.first]; i; i=edge[i].next) { int v=edge[i].to,np1,np2; int s=0,ss=0; if (u.second==0) s=count(dist[u.first][0],edge[i].dis,np1,at[u.first][0]); if (u.second==1) ss=count(dist[u.first][1],edge[i].dis,np2,at[u.first][1]); if (u.second==0&&dist[v][0]>s) { dist[v][0]=s; at[v][0]=np1; if (vis[v][0]==0) { vis[v][0]=1; Q.insert((zt){v,0}); } } if (u.second==1&&dist[v][1]>ss) { dist[v][1]=ss; at[v][1]=np2; if (vis[v][1]==0) { vis[v][1]=1; Q.insert((zt){v,1}); } } if (u.second==0&&dist[v][1]>dist[u.first][0]) { dist[v][1]=dist[u.first][0]; at[v][1]=at[u.first][0]; if (vis[v][1]==0) { vis[v][1]=1; Q.insert((zt){v,1}); } } } } } int main() { int u,v,i,j; int w; cin>>n>>m>>k; for (i=1; i<=m; i++) { u=gi(); v=gi(); w=gi(); add(u,v,w); add(v,u,w); } L[k+1]=2e9; for (i=1; i<=k; i++) { L[i]=gi(); R[i]=gi(); } for (i=1; i<=k; i++) st[0][i]=L[i+1]-R[i],p[i]=max(st[0][i],p[i-1]); for (i=1; i<=17; i++) { for (j=1; j<=k; j++) if (j+(1<<i)-1<=k) { st[i][j]=max(st[i-1][j],st[i-1][j+(1<<i-1)]); } else break; } SPFA(); //for (i=1;i<=n;i++) //cout<<dist[i][0]<<‘ ‘<<dist[i][1]<<endl; cout<<min(dist[n][1],dist[n][0]); }
以上是关于雨中冒险的主要内容,如果未能解决你的问题,请参考以下文章