P4768 [NOI2018] 归程
2022/7/24 23:25:57
本文主要是介绍P4768 [NOI2018] 归程,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
题目大意
\(n(n\le2\cdot10^5)\) 个点, \(m(m\le4\cdot10^5)\) 条边的无向图,每条边有长度 \(l(l\le10^4)\) ,海拔 \(a(a\le10^9)\) , \(q(q\le 4\cdot10^5)\) 次询问,每次从节点 \(v\) 出发,可以乘车经过任意连续一段海拔 \(> p\) 的边,之后便只能步行,求到达节点 \(1\) 所需的最短步行里程,强制在线。
思路
一开始乘车能够走到的节点集合即为按海拔降序建立 \(\texttt{Kruscal}\) 重构树后,以深度最小的权值 \(>p\) 的节点为根的子树。答案就是这些节点到 \(1\) 的最短路的最小值,于是我们可以先预处理出每个节点到 \(1\) 的最短路,然后在重构树上算出子树中最短路的最小值,之后对于每次询问,在重构树上倍增寻找深度最小的权值 \(>p\) 的节点即可 。
代码
#include<bits/stdc++.h> #include<unordered_map> #include<unordered_set> using namespace std; using LL = long long; using LD = long double; using ULL = unsigned long long; using PII = pair<LL, LL>; using TP = tuple<int, int, int>; #define all(x) x.begin(),x.end() #define mst(x,v) memset(x,v,sizeof(x)) #define mul(x,y) (1ll*(x)*(y)%mod) #define mk make_pair #define int LL //#define double LD //#define lc p*2 //#define rc p*2+1 #define endl '\n' #define inf 0x3f3f3f3f #define INF 0x3f3f3f3f3f3f3f3f #pragma warning(disable : 4996) #define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0) const double eps = 1e-8; const double pi = acos(-1); const LL MOD = 1000000007; const LL mod = 1004535809; const int maxn = 600010; const int maxm = 400010; int TT, N, M, Q, K, S; struct edge { int to, l, a; }; struct Edge { int u, v, cost; }es[maxm]; vector<edge>G[maxm]; vector<int>T[maxn]; void add_edge(int from, int to, int l, int a) { G[from].push_back(edge{ to,l,a }); G[to].push_back(edge{ from,l,a }); } void add_edge(int from, int to) { T[from].push_back(to); T[to].push_back(from); } bool cmp1(const Edge& x, const Edge& y) { return x.cost > y.cost; } int par[maxn]; void init(int n) { for (int i = 1; i <= n; i++) par[i] = i; } int find(int x) { if (x == par[x]) return x; return par[x] = find(par[x]); } void unite(int x, int y) { x = find(x), y = find(y); if (x == y) return; par[y] = x; } bool same(int x, int y) { return find(x) == find(y); } int val[maxn], cntv, cnte; void Kruskal() { sort(es + 1, es + M + 1, cmp1), init(cntv); for (int i = 1; i <= M; i++) { Edge& e = es[i]; int u = e.u, v = e.v; if (!same(u, v)) { int fu = find(u), fv = find(v); val[++cntv] = e.cost, par[cntv] = cntv; add_edge(fu, cntv), add_edge(cntv, fv); unite(cntv, fu), unite(cntv, fv); } if (cntv == N * 2 - 1) break; } } int d[maxn], fa[21][maxn], f[maxn]; void dijkstra() { for (int i = 1; i <= N; i++) d[i] = INF; priority_queue<PII, vector<PII>, greater<PII>>que; d[1] = 0, que.push(PII(0, 1)); while (!que.empty()) { PII p = que.top(); que.pop(); int v = p.second, dis = p.first; if (dis > d[v]) continue; for (auto& e : G[v]) { if (d[v] + e.l < d[e.to]) { d[e.to] = d[v] + e.l; que.push(PII(d[e.to], e.to)); } } } } void dfs(int v, int p) { fa[0][v] = p; if (v <= N) f[v] = d[v]; else f[v] = INF; for (auto& to : T[v]) { if (to == p) continue; dfs(to, v); f[v] = min(f[to], f[v]); } } void init() { dfs(cntv, 0); for (int k = 0; k + 1 < 20; k++) { for (int v = 1; v <= cntv; v++) { if (fa[k][v] <= 0) fa[k + 1][v] = 0; else fa[k + 1][v] = fa[k][fa[k][v]]; } } } void solve() { dijkstra(), Kruskal(), init(); int ans = 0; cin >> Q >> K >> S; int v, p; while (Q--) { cin >> v >> p; v = (v + K * ans - 1) % N + 1; p = (p + K * ans) % (S + 1); while (true) { int lst = v; for (int j = 20; j >= 0; j--) { if (fa[j][v] <= 0) continue; if (val[fa[j][v]] > p) v = fa[j][v]; } ans = f[v]; if (v == lst) break; } cout << ans << endl; } } signed main() { IOS; cin >> TT; while (TT--) { cin >> N >> M; for (int i = 1; i <= N + M; i++) T[i].clear(); for (int i = 1; i <= N; i++) G[i].clear(); cntv = N, cnte = M; int u, v, l, a; for (int i = 1; i <= M; i++) { cin >> u >> v >> l >> a; add_edge(u, v, l, a), es[i] = Edge{ u,v,a }; } solve(); } return 0; }
这篇关于P4768 [NOI2018] 归程的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-12-27数据结构与算法面试题详解及练习
- 2024-12-27网络请求面试题详解与实战
- 2024-12-27数据结构和算法面试真题详解与实战教程
- 2024-12-27网络请求面试真题解析与实战教程
- 2024-12-27数据结构和算法大厂面试真题详解与实战指南
- 2024-12-27TS大厂面试真题解析与应对策略
- 2024-12-27TS大厂面试真题详解与解析
- 2024-12-27网站安全入门:如何识别和修复漏洞
- 2024-12-27SQL注入基础教程
- 2024-12-27初学者指南:理解和修复跨域漏洞