Luogu 4768 [NOI2018]归程
并不会写Kruskal重构树,两个$log$跑得比较卡。
首先考虑一下没有强制在线的要求怎么办,有一个比较容易想到的做法就是先跑一遍最短路,然后把所有边按照海拔从大到小排序,把所有询问的海拔也从大到小排序,然后对于每一个询问$(x, h)$把所有海拔高于$h$的边都连上,然后看一看点$x$的能到达的点中离$1$距离最小的点是多少。
这就是一个并查集可以维护的东西了。
强制在线怎么办……直接大力把这个并查集可持久化……就没了。
时间复杂度$O(nlog^2n)$。
Code:
#include <cstdio> #include <cstring> #include <queue> #include <iostream> #include <algorithm> using namespace std; typedef pair <int, int> pin; const int N = 2e5 + 5; const int M = 5e5 + 5; int testCase, n, m, qn, tot, head[N], dis[N]; int numCnt, in[M], ed[M], pos[M]; bool vis[N]; struct Edge { int to, nxt, val; } e[M << 1]; inline void add(int from, int to, int val) { e[++tot].to = to; e[tot].val = val; e[tot].nxt = head[from]; head[from] = tot; } struct Pathway { int x, y, val, h, id; } path[M]; bool cmp(const Pathway &u, const Pathway &v) { return u.h < v.h; } inline void read(int &X) { X = 0; char ch = 0; int op = 1; for(; ch > '9' || ch < '0'; ch = getchar()) if(ch == '-') op = -1; for(; ch >= '0' && ch <= '9'; ch = getchar()) X = (X << 3) + (X << 1) + ch - 48; X *= op; } priority_queue <pin> Q; void dij() { memset(dis, 0x3f, sizeof(dis)); memset(vis, 0, sizeof(vis)); Q.push(pin(dis[1] = 0, 1)); for(; !Q.empty(); ) { int x = Q.top().second; Q.pop(); if(vis[x]) continue; vis[x] = 1; for(int i = head[x]; i; i = e[i].nxt) { int y = e[i].to; if(dis[y] > dis[x] + e[i].val) { dis[y] = dis[x] + e[i].val; Q.push(pin(-dis[y], y)); } } } } inline int min(int x, int y) { return x > y ? y : x; } inline void swap(int &x, int &y) { int t = x; x = y; y = t; } namespace PUfs { struct Node { int lc, rc, ufs, dis, dep; } s[N * 60]; int root[M], nodeCnt = 0LL; #define lc(p) s[p].lc #define rc(p) s[p].rc #define ufs(p) s[p].ufs #define dis(p) s[p].dis #define dep(p) s[p].dep #define mid ((l + r) >> 1) inline void clear(int p) { lc(p) = rc(p) = ufs(p) = dis(p) = dep(p) = 0; } void build(int &p, int l, int r, int type) { p = ++nodeCnt; if(l == r) { ufs(p) = l, dep(p) = 0; if(!type) dis(p) = 0; else dis(p) = dis[l]; return; } build(lc(p), l, mid, type); build(rc(p), mid + 1, r, type); } void modify(int &p, int l, int r, int x, int nowUfs, int nowDis, int pre) { p = ++nodeCnt; if(l == r) { ufs(p) = nowUfs, dis(p) = nowDis, dep(p) = dep(pre); return; } lc(p) = lc(pre), rc(p) = rc(pre); if(x <= mid) modify(lc(p), l, mid, x, nowUfs, nowDis, lc(pre)); else modify(rc(p), mid + 1, r, x, nowUfs, nowDis, rc(pre)); } void mDis(int &p, int l, int r, int x, int nowDis, int pre) { p = ++nodeCnt; if(l == r) { ufs(p) = ufs(pre), dep(p) = dep(pre), dis(p) = nowDis; return; } lc(p) = lc(pre), rc(p) = rc(pre); if(x <= mid) mDis(lc(p), l, mid, x, nowDis, lc(pre)); else mDis(rc(p), mid + 1, r, x, nowDis, rc(pre)); } void add(int p, int l, int r, int x) { if(l == r) { ++dep(p); return; } if(x <= mid) add(lc(p), l, mid, x); else add(rc(p), mid + 1, r, x); } int query(int p, int l, int r, int x) { if(l == x && x == r) return p; if(x <= mid) return query(lc(p), l, mid, x); else return query(rc(p), mid + 1, r, x); } int find(int now, int x) { int f = query(now, 1, n, x); if(s[f].ufs == x) return f; else return find(now, s[f].ufs); } inline void merge(int k, int x, int y) { root[k] = root[k + 1]; int fx = find(root[k], x), fy = find(root[k], y); if(s[fx].ufs == s[fy].ufs) return; if(s[fx].dep > s[fy].dep) swap(fx, fy); int dis = min(s[fx].dis, s[fy].dis); modify(root[k], 1, n, s[fx].ufs, s[fy].ufs, dis, root[k + 1]); if(dis != s[fy].dis) mDis(root[k], 1, n, s[fy].ufs, dis, root[k]); if(s[fx].dep == s[fy].dep) add(root[k], 1, n, s[fy].ufs); } #undef mid } using namespace PUfs; inline void prework() { for(int i = 1; i <= nodeCnt; i++) clear(i); nodeCnt = 0; memset(root, 0, sizeof(root)); build(root[m + 1], 1, n, 1); build(root[0], 1, n, 0); } inline int bfind(int val) { int ln = 0, rn = numCnt, mid, res; for(; ln <= rn; ) { mid = (ln + rn) / 2; if(in[mid] <= val) ln = mid + 1, res = mid; else rn = mid - 1; } return res; } int main() { // freopen("return.in", "r", stdin); // freopen("return.out", "w", stdout); for(read(testCase); testCase--; ) { tot = numCnt = 0; memset(head, 0, sizeof(head)); read(n), read(m); for(int i = 1; i <= m; i++) { read(path[i].x), read(path[i].y), read(path[i].val), read(path[i].h); in[++numCnt] = path[i].h; path[i].id = i; add(path[i].x, path[i].y, path[i].val), add(path[i].y, path[i].x, path[i].val); } sort(in + 1, in + 1 + numCnt); numCnt = unique(in + 1, in + 1 + numCnt) - in - 1; /* for(int i = 1; i <= numCnt; i++) printf("%d ", in[i]); printf("\n"); */ for(int i = 1; i <= m; i++) path[i].h = lower_bound(in + 1, in + 1 + numCnt, path[i].h) - in; dij(); /* for(int i = 1; i <= n; i++) printf("%d ", dis[i]); printf("\n"); */ prework(); sort(path + 1, path + 1 + m, cmp); /* printf("\n"); for(int i = 1; i <= m; i++) printf("%d %d %d %d\n", path[i].x, path[i].y, path[i].val, path[i].h); */ for(int i = m; i >= 1; i--) { merge(i, path[i].x, path[i].y); if(path[i].h != path[i - 1].h) ed[path[i].h] = i; } /* for(int i = 1; i <= numCnt; i++) printf("%d ", ed[i]); printf("\n"); */ for(int i = 1; i < numCnt; i++) pos[i] = ed[i + 1]; pos[numCnt] = m + 1; /* for(int i = 1; i <= numCnt; i++) printf("%d ", pos[i]); printf("\n"); */ /* for(int j = 1; j <= n; j++) printf("%d ", s[query(root[m + 1], 1, n, j)].dis); printf("\n"); */ /* for(int i = 1; i <= numCnt; i++) { printf("%d: ", i); for(int j = 1; j <= n; j++) printf("%d ", s[query(root[pos[i]], 1, n, j)].ufs); printf("\n"); } for(int i = 1; i <= numCnt; i++) { printf("%d: ", i); for(int j = 1; j <= n; j++) printf("%d ", s[query(root[pos[i]], 1, n, j)].dis); printf("\n"); } */ /* for(int i = 1; i <= numCnt; i++) printf("%d ", pos[i]); printf("\n"); */ // in[++numCnt] = path[m].h + 1; int K, S, ans = 0; read(qn), read(K), read(S); for(int x, h; qn--; ) { read(x), read(h); x = (x + K * ans - 1) % n + 1, h = (h + K * ans) % (S + 1); int rt = bfind(h); rt = pos[rt]; rt = find(root[rt], x); printf("%d\n", ans = s[rt].dis); } } return 0; }
缅怀$spfa$。