UOJ #134
NOIP模拟题 2015.8.2 T3

NOIP模拟题 2015.8.1 T2

HJWJBSR posted @ 2015年8月16日 22:07 in 题解 , 487 阅读

感觉题解还是要放的正常向一点,还是一题一题放吧= =

题意:给定一张无向图,每条边有通过这条边消耗的油量,有一些点是加油点,可以补满油,然后有q个询问从一个加油点往另外一个加油点走,油箱容量为v,问能否到达。

输入格式:第一行四个整数n m p k分别是点、边、询问和加油点数,第二行k个加油点的编号,接着m行每行三个整数u、v和d代表u和v之间有耗油为d的边相连,最后p行每行三个整数u、v、d询问u到v在容量只有d的时候能否到达

输出格式:q行“YES”或者“NO”

Sample Input:

6 5 4 4
1 5 2 6
1 3 1
2 3 2
3 4 3
4 5 5
6 4 5
1 2 4
2 6 9
1 5 9
6 5 8
 
Sample Output:
 
YES
YES
YES
NO
 

题解:这题之前VFK讲课时讲过,不,应该说则就是VFK出的题。然而并忘(睡)了= =,而且T3是一道恶心的点分治/set启发式合并题,写的比较蛋痛,也没时间想这道题了。

然而这题有个结论:设点i的最近加油点为Ai,这两点的距离为Si,于是我们考虑重建一个只有加油点的图,原来的连i-j的边变成Ai-Aj,边权变成Si+Sj+Vi-j。由于脑补可以发现这样是不会使两点之间距离变大,如果有一条经过非加油点u、v的路径i->u->v->j并且i和j不是u、v的最近点的话画一画图可以发现在走去Ai和Aj一定是会更优的

于是设一个S连向所有加油点,SPFA一下求出所有点的Ai,然后就是最小生成树还有倍增了= =

话说在这里贴一张VFK题解:

#include <iostream>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <cstdio>
#include <queue>
using namespace std;
#define N 300050
#define M 1000050
#define fr first
#define sc second
#define INF 0x7f7f7f7f
pair <int,int> yz[M];
int ne[M*2][3],ln[M][3],fi[N],nr[N],sg[N],n,m,ff[N];
int ss=1,s[N],gf,qu,zy,fa[N][20],ma[N][20],h[N];
queue <int> li;
bool b[N];
inline int Read()
 {
   	int x=0;char y;
 	  do y=getchar(); while (y<'0'||y>'9');
   	do x=x*10+y-'0',y=getchar(); while (y>='0'&&y<='9');
    return x;
 }
inline void Line(int x,int y,int z)
 {
 	  ne[++ss][0]=y;ne[ss][1]=fi[x];ne[ss][2]=z;fi[x]=ss;
 	  ne[++ss][0]=x;ne[ss][1]=fi[y];ne[ss][2]=z;fi[y]=ss;
 }
void SPFA()
 {
   	for (int i=1;i<=n;i++) s[i]=INF;
   	for (int i=1;i<=zy;i++)
   	  s[sg[i]]=0,li.push(sg[i]),b[sg[i]]=1,nr[sg[i]]=sg[i];
   	while (!li.empty())
   	 {
     	 	int k=li.front();li.pop();b[k]=0;
 	     	for (int i=fi[k];i;i=ne[i][1])
 	 	     if (s[ne[i][0]]>s[k]+ne[i][2])
 	 	      {
 	 	  	     s[ne[i][0]]=s[k]+ne[i][2];nr[ne[i][0]]=nr[k];
 	 	  	     if (!b[ne[i][0]])
 	 	  	       b[ne[i][0]]=1,li.push(ne[i][0]);
 	 	      }
   	 }
 	  return;
 }
int Find(int x)
 {return ff[x]==x?x:(ff[x]=Find(ff[x]));}
void Kruskal()
 {
   	sort(yz+1,yz+m+1);
   	for (int i=1;i<=m;i++)
   	 {
   	   	int k=nr[ln[yz[i].sc][0]],l=nr[ln[yz[i].sc][1]],
   	      q=Find(k),w=Find(l);
 	      if (q==w) continue;
 	      ff[q]=w;Line(k,l,yz[i].fr);
 	   }
   	return;
 }
void Set_up()
 {
 	  memset(s,0,sizeof(s));
 	  gf=sg[1];li.push(gf);s[gf]=1;
 	  while (!li.empty())
 	   {
 	 	    int k=li.front();li.pop();
 	 	    for (int i=fi[k];i;i=ne[i][1])
 	 	     if (!s[ne[i][0]])
 	 	       s[ne[i][0]]=s[k]+1,fa[ne[i][0]][0]=k,
 	 	       ma[ne[i][0]][0]=ne[i][2],li.push(ne[i][0]);
   	 }
 	  for (int i=1;i<=18;i++)
 	   {
 	      for (int j=1;j<=zy;j++)
 	 	      ma[sg[j]][i]=max(ma[sg[j]][i-1],
 	 	       ma[fa[sg[j]][i-1]][i-1]),
 	 	        fa[sg[j]][i]=fa[fa[sg[j]][i-1]][i-1];
 	   }
 	  return;
 }
int Query(int x,int y)
 {
 	  int k=0;
 	  if (s[x]<s[y]) swap(x,y);
 	  for (int i=18;i>=0;i--)
 	   if (s[x]-(1 << i)>=s[y])
 	     k=max(k,ma[x][i]),x=fa[x][i];
 	  for (int i=18;i>=0;i--)
 	   if (fa[x][i]!=fa[y][i])
 	     k=max(max(k,ma[x][i]),ma[y][i]),x=fa[x][i],y=fa[y][i];
 	  if (x!=y)
 	   k=max(max(k,ma[x][0]),ma[y][0]);
   	return k;
 }
int main()
 {
    freopen("petrol.in","r",stdin);freopen("petrol.out","w",stdout);
 	  n=Read();m=Read();zy=Read();qu=Read();
 	  for (int i=1;i<=zy;i++) sg[i]=Read(),ff[sg[i]]=sg[i];
 	  for (int i=1;i<=m;i++)
      ln[i][0]=Read(),ln[i][1]=Read(),ln[i][2]=Read(),
      Line(ln[i][0],ln[i][1],ln[i][2]);
    SPFA();ss=1;
    memset(fi,0,sizeof(fi));
    for (int i=1;i<=m;i++)
      yz[i].sc=i,yz[i].fr=ln[i][2]+s[ln[i][0]]+s[ln[i][1]];
    Kruskal();Set_up();
    while (qu--)
     {
     	  int q=Read(),w=Read(),e=Read();
     	  if (e>=Query(q,w)) puts("YES");else
     	    puts("NO");
     }
    return 0;
 }

登录 *


loading captcha image...
(输入验证码)
or Ctrl+Enter