4144: [AMPPZ2014]Petrol (多源最短路+最小生成树+启发式合并)

4144: [AMPPZ2014]Petrol

Time Limit: 10 Sec  Memory Limit: 256 MB
Submit: 752  Solved: 298
[Submit][Status][Discuss]

Description

给定一个n个点、m条边的带权无向图,其中有s个点是加油站。

每辆车都有一个油量上限b,即每次行走距离不能超过b,但在加油站可以补满。

q次询问,每次给出x,y,b,表示出发点是x,终点是y,油量上限为b,且保证x点和y点都是加油站,请回答能否从x走到y。

Input

第一行包含三个正整数n,s,m(2<=s<=n<=200000,1<=m<=200000),表示点数、加油站数和边数。

第二行包含s个互不相同的正整数c[1],c[2],...c[s](1<=c[i]<=n),表示每个加油站。

接下来m行,每行三个正整数u[i],v[i],d[i](1<=u[i],v[i]<=n,u[i]!=v[i],1<=d[i]<=10000),表示u[i]和v[i]之间有一条长度为d[i]的双向边。

接下来一行包含一个正整数q(1<=q<=200000),表示询问数。

接下来q行,每行包含三个正整数x[i],y[i],b[i](1<=x[i],y[i]<=n,x[i]!=y[i],1<=b[i]<=2*10^9),表示一个询问。

Output

输出q行。第i行输出第i个询问的答案,如果可行,则输出TAK,否则输出NIE。

Sample Input

6 4 5

1 5 2 6

1 3 1

2 3 2

3 4 3

4 5 5

6 4 5

4

1 2 4

2 6 9

1 5 9

6 5 8

Sample Output

TAK

TAK

TAK

NIE

HINT

Source

鸣谢Claris上传



朴素是先写个多源最短路把加油站的最小生成树所有可能边弄出来,然后后面按部就班地建个最小生成树,然后对树上写个主席树(st倍增)找u到v上最大值,和b比较一下。

那我们在把所有可能边弄出来以后停一下2333,我们连边不要对这条边俩端点连,而是对他们所在块的堆头节点连,这个建出来在查询最大值上是等效的。然后我们启发式地合并堆,这样可以把堆高度控制在logn,就不用再写个主席树啥的那麻烦了。直接两个端点往上一个一个跳,logn的不会有事的。

  1 #include<bits/stdc++.h>
  2 #define clr(x) memset(x,0,sizeof(x))
  3 #define clr_1(x) memset(x,-1,sizeof(x))
  4 #define INF 0x3f3f3f3f
  5 #define LL long long
  6 #define pb push_back
  7 #define mod 1000000007
  8 #define ls(i) (i<<1)
  9 #define rs(i) (i<<1|1)
 10 #define mp make_pair
 11 #define fi first
 12 #define se second
 13 using namespace std;
 14 typedef pair<LL,int > pli;
 15 const int N = 2e5+10;
 16 bool need[N];
 17 vector<pli> e[N];
 18 struct node
 19 {
 20     int u,v;
 21     LL w;
 22     node(int _u=0,int _v=0,LL _w=0):u(_u),v(_v),w(_w) {}
 23 };
 24 vector<node> ve;
 25 int n,s,m,T;
 26 priority_queue<pli,vector<pli>,greater<pli> > que;
 27 int fuel[N];
 28 int fa[N],rfa[N],rk[N],dep[N];
 29 LL dis[N],val[N];
 30 bool vis[N];
 31 int pre[N];
 32 void dij()
 33 {
 34     while(!que.empty())
 35     {
 36         pli now=que.top();
 37         que.pop();
 38         LL dist=now.fi;
 39         int u=now.se;
 40         if(vis[u]) continue;
 41         vis[u]=1;
 42         int sz=e[u].size();
 43         for(int i=0;i<sz;i++)
 44         {
 45             pli p=e[u][i];
 46             int w=p.fi;
 47             int v=p.se;
 48             if(!pre[v] || dis[v]>dis[u]+w)
 49             {
 50                 dis[v]=dis[u]+w;
 51                 pre[v]=pre[u];
 52                 que.push(mp(dis[v],v));
 53             }
 54             else if(pre[u] != pre[v])
 55                 ve.pb(node(pre[u],pre[v],dis[u]+dis[v]+w));
 56         }
 57     }
 58     return ;
 59 }
 60 bool cmp(node a,node b)
 61 {
 62     return a.w<b.w;
 63 }
 64 int Find(int u)
 65 {
 66     if(fa[u]!=u)
 67         fa[u]=Find(fa[u]);
 68     return fa[u];
 69 }
 70 void Union()
 71 {
 72     sort(ve.begin(),ve.end(),cmp);
 73     for(int i=1;i<=s;i++)
 74     {
 75         fa[fuel[i]]=fuel[i];
 76         rk[fuel[i]]=1;
 77     }
 78     int sz=ve.size();
 79     for(int i=0;i<sz;i++)
 80     {
 81         node p=ve[i];
 82         int u = p.u, v = p.v;
 83         LL w = p.w;
 84         u = Find(u), v = Find(v);
 85         if(u==v) continue;
 86         if(rk[u]<rk[v]) swap(u,v);
 87         if(rk[u]==rk[v]) rk[u]++;
 88         rfa[v]=u,fa[v]=u,val[v]=w;
 89     }
 90     return ;
 91 }
 92 void dealdep(int u)
 93 {
 94     if(dep[u]>0) return ;
 95     if(fa[u]==u)
 96     {
 97         dep[u]=1;
 98         return ;
 99     }
100     dealdep(rfa[u]);
101     dep[u]=dep[rfa[u]]+1;
102     return ;
103 }
104 bool solve(int u,int v,LL b)
105 {
106     if(Find(u)!=Find(v)) return 0;
107     if(dep[u]<dep[v]) swap(u,v);
108     while(dep[u]>dep[v])
109     {
110         if(b<val[u]) return 0;
111         u=rfa[u];
112     }
113     if(u==v) return 1;
114     while(u!=v)
115     {
116         if(b<val[u]) return 0;
117         if(b<val[v]) return 0;
118         u=rfa[u];
119         v=rfa[v];
120     }
121     return 1;
122 }
123 int main()
124 {
125     scanf("%d%d%d",&n,&s,&m);
126     clr_1(dis);
127     for(int i=1;i<=s;i++)
128     {
129         scanf("%d",fuel+i);
130         que.push(mp(0,fuel[i]));
131         pre[fuel[i]]=fuel[i];
132         dis[fuel[i]]=0;
133     }
134     for(int i=1;i<=m;i++)
135     {
136         int u,v;
137         LL w;
138         scanf("%d%d%lld",&u,&v,&w);
139         e[u].pb(mp(w,v));
140         e[v].pb(mp(w,u));
141     }
142     dij();
143     Union();
144     for(int i=1;i<=s;i++)
145         dealdep(fuel[i]);
146     int u,v;
147     LL b,maxn;
148     int q;
149     scanf("%d",&q);
150     while(q--)
151     {
152         scanf("%d%d%lld",&u,&v,&b);
153         if(solve(u,v,b))
154             printf("TAK\n");
155         else
156             printf("NIE\n");
157     }
158     return 0;
159 }

原文地址:https://www.cnblogs.com/wujiechao/p/9170670.html

时间: 2024-10-28 22:30:24

4144: [AMPPZ2014]Petrol (多源最短路+最小生成树+启发式合并)的相关文章

BZOJ 4144: [AMPPZ2014]Petrol

4144: [AMPPZ2014]Petrol Time Limit: 10 Sec  Memory Limit: 256 MBSubmit: 457  Solved: 170[Submit][Status][Discuss] Description 给定一个n个点.m条边的带权无向图,其中有s个点是加油站. 每辆车都有一个油量上限b,即每次行走距离不能超过b,但在加油站可以补满. q次询问,每次给出x,y,b,表示出发点是x,终点是y,油量上限为b,且保证x点和y点都是加油站,请回答能否从x走

【BZOJ4144】[AMPPZ2014]Petrol 最短路+离线+最小生成树

[BZOJ4144][AMPPZ2014]Petrol Description 给定一个n个点.m条边的带权无向图,其中有s个点是加油站. 每辆车都有一个油量上限b,即每次行走距离不能超过b,但在加油站可以补满. q次询问,每次给出x,y,b,表示出发点是x,终点是y,油量上限为b,且保证x点和y点都是加油站,请回答能否从x走到y. Input 第一行包含三个正整数n,s,m(2<=s<=n<=200000,1<=m<=200000),表示点数.加油站数和边数. 第二行包含s

常见模板(欧拉筛素数,最小生成树,快排,并查集,单源最短路)

欧拉筛素数: #include<cstdio> #define maxn 10000000+10 using namespace std; int n,prime[5000001],num_prime=0,m; bool if_prime[maxn]; void euler(int limit) { for(int i=2;i<=limit;i++) { if(!if_prime[i]) prime[++num_prime]=i; for(int j=1;prime[j]*i<=l

bzoj4144 [AMPPZ2014]Petrol 图论 最短路 并查集

bzoj4144 [AMPPZ2014]Petrol 图论 最短路 并查集 1.这道题我们主要就是要求出距离一个油站的最近的油站 首先我们dijkstra 求出任意一个点到 离他最近的油站的距离 2.然后会发现 如果一条边的两个端点 的最近油站不同的话 那么这条边就会在这两个油站的最短路上 3.然后对于每一条边 我们将他的权值 变为 dis[ u ] + dis[ v ] + e[ i ][ j ] 如果u与v最近油站相同 那么这个无意义 如果不同 那么久表示 u 最近油站 到 v 最近油站的最

bzoj4144 [AMPPZ2014]Petrol

Description 给定一个 \(n\)个 点. \(m\) 条边的带权无向图,其中有 \(s\) 个点是加油站. 每辆车都有一个油量上限 \(b\) ,即每次行走距离不能超过 \(b\) ,但在加油站可以补满. \(q\) 次询问,每次给出 \(x,y,b\) ,表示出发点是 \(x\) ,终点是 \(y\) ,油量上限为 \(b\) ,且保证 \(x\) 点和 \(y\) 点都是加油站,请回答能否从 \(x\) 走到 \(y\) . Input 第一行包含三个正整数 \(n,s,m(2\

【算法系列学习】Dijkstra单源最短路 [kuangbin带你飞]专题四 最短路练习 A - Til the Cows Come Home

https://vjudge.net/contest/66569#problem/A http://blog.csdn.net/wangjian8006/article/details/7871889 邻接矩阵实现的单源最短路 1 #include<iostream> 2 #include<cstdio> 3 #include<string> 4 #include<cstring> 5 #include<algorithm> 6 #include

Dijkstra算法 --- 单源最短路

Dijkstra算法适用于边权值为正的情况,可用于计算正权图上的单元最短路. 其伪代码如下: 设d[v0] = 0, 其他d[i] = INF 循环n次{ 在所有未标号的结点中,选取d值最小的结点x 给结点x加上永久标号 对于从x出发的所有边,执行松弛操作. } //松弛操作的伪代码如下: RELAX(u,v,w) if(u.d + w(u,v) < v.d){ v.d = w.d + w(u,v); pre[v] = u; } Dijkstra算法代码: /* Dijkstra 单源最短路算法

利用分支限界法求解单源最短路(Dijkstra)问题

分支限界法定义:采用BFS算法,并使用剪枝函数的算法称为分支界限法. 分支限界法解释:按广度优先的原则,有选择的在其child中进行扩展,从而舍弃不含有最优解的分支,不断重复这一过程,直到找到答案或者判定无解. 分支界限法常常用到优先队列来选择最佳扩展节点,有时也会用到普通队列,以先进先出为原则来进行筛选. 单源最短路问题定义:给定有向图和起点,寻找到达所有点的最短路径. 单源最短路的分支限界法概述:首先把节点加入优先队列,之后不断地从队列中取出最优扩展点,观察其可抵达的所有目标节点,若当前路径

UVA 658 It&#39;s not a Bug, it&#39;s a Feature! (单源最短路,dijkstra+优先队列,变形,经典)

题意:有n个bug,有m个补丁,每个补丁有一定的要求(比如某个bug必须存在,某个必须不存在,某些无所谓等等),打完出来后bug还可能变多了呢.但是打补丁是需要时间的,每个补丁耗时不同,那么问题来了:要打多久才能无bug?(同1补丁可重复打) 分析: n<=20,那么用位来表示bug的话有220=100万多一点.不用建图了,图实在太大了,用位图又不好玩.那么直接用隐式图搜索(在任意点,只要满足转移条件,任何状态都能转). 但是有没有可能每个状态都要搜1次啊?那可能是100万*100万啊,这样出题