【YBT2022寒假Day9 B】【luogu CF464E】进制路径 / The Classic Problem(最短路)(主席树)(哈希)
进制路径 / The Classic Problem
题目链接:YBT2022寒假Day9 B / luogu CF464E
题目大意
给你一个无向图,然后边的权值是 2^x 次方,然后要你求一个点到另一个点的最短路径。
思路
首先暴力做法直接跑 Dij。
然后问题在什么地方呢?
边权加在一起和比较。
考虑它是二进制,考虑模拟一下二进制加的过程。
如果是 \(0\) 直接加,如果加一就进位,然后不断进位,进位到第一个 \(0\)。
然后再看看比大小。
从高位往低位比,如果相同看下一位,不同直接比。
然后我们发现可以用线段树来维护,第一个直接可以在线段树上二分得出最右的 \(1\) 到哪里。
然后第二个我们可以直接二分然后比较后面若干位是否相同,然后比较相同可以用个哈希。(注意哈希每次的底数不是 \(2\) 就行,因为你是自然溢出,是 \(\bmod 2^{64}\),那高位的就直接丢失了)
但是线段树每个点都维护也会超空间,那我们考虑能不能用主席树。
发现每次就是加一下,改变的量是比较少的,所以我们用主席树即可。
代码
#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;
}