码迷,mamicode.com
首页 > 其他好文 > 详细

某校内题 rain

时间:2017-06-05 23:44:59      阅读:481      评论:0      收藏:0      [点我收藏+]

标签:覆盖   大于   开始   push   logs   范围   tin   pac   ios   

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;
}

 

某校内题 rain

标签:覆盖   大于   开始   push   logs   范围   tin   pac   ios   

原文地址:http://www.cnblogs.com/qt666/p/6947320.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!