Codeforces 464E The Classic Problem (最短路 + 主席树 + hash)
这个题加深了我对主席树的理解,是个好题。每次更新某个点的距离时,是以之前对这个点的插入操作形成的线段树为基础,在O(logn)的时间中造出了一颗新的线段树,相比直接创建n颗线段树更省时间。比较的时候二分比较,为了加快比较给每个点设置一个hash值。
代码:
#include <bits/stdc++.h> using namespace std; const unsigned long long P = 13331; const int mod = 1000000007; const int maxn = 100010; int head[maxn], Next[maxn * 2], edge[maxn * 2], ver[maxn * 2]; int tot, tote; int b[maxn * 2], pre[maxn]; int mx, dis[maxn]; bool vis[maxn]; int Stack[maxn], Top; void add(int x, int y, int z) { ver[++tote] = y; edge[tote] = z; Next[tote] = head[x]; head[x] = tote; } struct SegementTree { int ls, rs; int sum; unsigned long long hash; }tr[maxn * 200]; void pushup(int x) { tr[x].sum = tr[tr[x].ls].sum + tr[tr[x].rs].sum; unsigned long long tmp1 = tr[tr[x].ls].hash, tmp2 = tr[tr[x].rs].hash; tr[x].hash = (((tmp1 * P + tmp2) ^ tmp1) * P ^ tmp2) * P; } void insert(int &now, int l, int r, int pos) { int p = now; now = ++tot; tr[now] = tr[p]; if(l == r) { tr[now].sum = 1; tr[now].hash = l + P; return; } int mid = (l + r) >> 1; if(pos <= mid) insert(tr[now].ls, l, mid, pos); else insert(tr[now].rs, mid + 1, r, pos); pushup(now); } void del(int &now, int l, int r, int ql, int qr) { if(l >= ql && r <= qr) { now = 0; return; } int p = now;now = ++tot; tr[now] = tr[p]; int mid = (l + r) >> 1; if(ql <= mid) del(tr[now].ls, l, mid, ql, qr); if(qr > mid) del(tr[now].rs, mid + 1, r, ql, qr); pushup(now); } int query(int now, int l, int r, int pos) { if(l == r) { if(tr[now].sum == 0) return l; return -1; } int mid = (l + r) >> 1; if(pos > mid) return query(tr[now].rs, mid + 1, r, pos); int ans = query(tr[now].ls, l, mid, pos); if(ans != -1) return ans; return query(tr[now].rs, mid + 1, r, pos); } int add(int now, int pos) { int p = query(now, 0, mx, pos); if(p > pos) del(now, 0, mx, pos, p - 1); insert(now, 0, mx, p); return now; } int get_sum(int now, int l, int r) { int mid = (l + r) >> 1; if(now == 0) return 0; if(l == r) return b[l]; return (get_sum(tr[now].ls, l, mid) + get_sum(tr[now].rs, mid + 1, r)) % mod; } bool cmp(int x, int y, int l, int r) { if(l == r) return tr[x].sum >= tr[y].sum; int mid = (l + r) >> 1; if(tr[tr[x].rs].hash != tr[tr[y].rs].hash) return cmp(tr[x].rs, tr[y].rs, mid + 1, r); else return cmp(tr[x].ls, tr[y].ls, l, mid); } struct node { int x, y; bool operator < (const node& rhs) const { return cmp(x, rhs.x, 0, mx); } }; priority_queue<node> q; void dijkstra(int s) { dis[s] = ++tot, q.push((node){dis[s], s}); while(!q.empty()) { node tmp = q.top(); q.pop(); if(vis[tmp.y]) continue; vis[tmp.y] = 1; int x = tmp.y; for (int i = head[x]; i; i = Next[i]) { int y = ver[i], z = add(dis[x], edge[i]); if(!dis[y] || !cmp(z, dis[y], 0, mx)) { pre[y] = x; dis[y] = z; q.push((node){z, y}); } } } } void print(int x, int deep) { if(x == 0) { printf("%d\n", deep); return; } print(pre[x], deep + 1); printf("%d ", x); } int main() { int n, m, x, y, z, s, t; scanf("%d%d", &n, &m); for (int i = 1; i <= m; i++) { scanf("%d%d%d", &x, &y, &z); add(x, y, z); add(y, x, z); mx = max(mx, z); } b[0] = 1; mx += 20; for (int i = 1; i <= mx; i++) { b[i] = (b[i - 1] << 1) % mod; } scanf("%d%d", &s, &t); dijkstra(s); if(dis[t] == 0) printf("-1\n"); else { printf("%d\n", get_sum(dis[t], 0, mx)); while(t) { Stack[++Top] = t; t = pre[t]; } printf("%d\n", Top); while(Top) { printf("%d ", Stack[Top]); Top--; } } }