【YBT2022寒假Day9 B】【luogu CF464E】进制路径 / The Classic Problem(最短路)(主席树)(哈希)

进制路径 / The Classic Problem

题目链接:YBT2022寒假Day9 B / luogu CF464E

题目大意

给你一个无向图,然后边的权值是 2^x 次方,然后要你求一个点到另一个点的最短路径。

思路

首先暴力做法直接跑 Dij。

然后问题在什么地方呢?
边权加在一起和比较。

考虑它是二进制,考虑模拟一下二进制加的过程。
如果是 0 直接加,如果加一就进位,然后不断进位,进位到第一个 0
然后再看看比大小。
从高位往低位比,如果相同看下一位,不同直接比。

然后我们发现可以用线段树来维护,第一个直接可以在线段树上二分得出最右的 1 到哪里。
然后第二个我们可以直接二分然后比较后面若干位是否相同,然后比较相同可以用个哈希。(注意哈希每次的底数不是 2 就行,因为你是自然溢出,是 mod264,那高位的就直接丢失了)

但是线段树每个点都维护也会超空间,那我们考虑能不能用主席树。
发现每次就是加一下,改变的量是比较少的,所以我们用主席树即可。

代码

#include<queue> #include<cstdio> #include<vector> #include<cstring> #define ll long long #define ull unsigned long long #define di 131 #define mo 1000000007 using namespace std; struct node { ll x, to, nxt; }e[200001]; ll n, m, s, t, x, y, z, fr[100001]; ll le[100001], KK; struct nde { ll rt, id; }; priority_queue <nde, vector<nde>, greater<nde> > q; bool in[100001], dnt[100001]; ull dii[200002], one[200002]; int rt[200001], answer; struct ZX_tree { int ls[200001 << 7], rs[200001 << 7], tot; ull hash[200001 << 7]; int clone(int x) { int now = ++tot; ls[now] = ls[x]; rs[now] = rs[x]; hash[now] = hash[x]; return now; } bool da(int x, int y, int l, int r) { if (hash[x] == hash[y]) return 0; if (l == r) return hash[x] > hash[y]; int mid = (l + r) >> 1; if (hash[rs[x]] == hash[rs[y]]) return da(ls[x], ls[y], l, mid); return da(rs[x], rs[y], mid + 1, r); } bool jia(int x, int &now, int l, int r, int pl) { if (l >= pl && one[r - l] == hash[x]) { now = 0; return 0; } now = clone(x); if (l == r) { hash[now] = 1; return 1; } int mid = (l + r) >> 1; bool yes = 0; if (l < pl) { if (pl <= mid) yes = jia(ls[x], ls[now], l, mid, pl); if (!yes) yes = jia(rs[x], rs[now], mid + 1, r, pl); } else { if (hash[ls[x]] == one[mid - l]) { ls[now] = 0; yes = jia(rs[x], rs[now], mid + 1, r, pl); } else yes = jia(ls[x], ls[now], l, mid, pl); } hash[now] = hash[ls[now]] + hash[rs[now]] * dii[mid - l + 1]; return yes; } void clac(int now, int l, int r) { if (l == r) { answer = (answer * 2 + hash[now]) % mo; return ; } int mid = (l + r) >> 1; clac(rs[now], mid + 1, r); clac(ls[now], l, mid); } }T; bool operator >(nde x, nde y) { return T.da(x.rt, y.rt, 0, 200000); } void add(ll x, ll y, ll z) { e[++KK] = (node){z, y, le[x]}; le[x] = KK; } int main() { // freopen("base.in", "r", stdin); // freopen("base.out", "w", stdout); scanf("%lld %lld %lld %lld", &n, &m, &s, &t); for (int i = 1; i <= m; i++) { scanf("%lld %lld %lld", &x, &y, &z); add(x, y, z); add(y, x, z); } dii[0] = 1; for (int i = 1; i <= 200001; i++) dii[i] = dii[i - 1] * di; one[0] = 1; for (int i = 1; i <= 200001; i++) one[i] = one[i - 1] * di + 1; rt[1] = ++T.tot; for (int i = 2; i <= n; i++) rt[i] = rt[1]; dnt[s] = 1; q.push((nde){rt[s], s}); while (!q.empty()) { int now = q.top().id; q.pop(); if (in[now]) continue; in[now] = 1; for (int i = le[now]; i; i = e[i].nxt) { if (in[e[i].to]) continue; int ne; T.jia(rt[now], ne, 0, 200000, e[i].x); if (!dnt[e[i].to] || T.da(rt[e[i].to], ne, 0, 200000)) { dnt[e[i].to] = 1; rt[e[i].to] = ne; fr[e[i].to] = now; q.push((nde){rt[e[i].to], e[i].to}); } } } if (!dnt[t]) { printf("-1"); return 0; } T.clac(rt[t], 0, 200000); printf("%d\n", answer); int now = t; vector <int> ans; while (now != s) { ans.push_back(now); now = fr[now]; } ans.push_back(s); printf("%d\n", ans.size()); for (int i = ans.size() - 1; i >= 0; i--) printf("%d ", ans[i]); return 0; }

__EOF__

本文作者あおいSakura
本文链接https://www.cnblogs.com/Sakura-TJH/p/YBT2022Day9_B.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   あおいSakura  阅读(28)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示