BZOJ 4012权限题
浙科协的网突然炸了,好慌……
据说正解是动态点分治,然而我并不会,我选择树链剖分 + 主席树维护。
设$dis_i$表示$i$到$root(1)$的值,那么对于一个询问$u$,答案为$sum_{i = 1}^{n}dis_i + n * dis_u - 2 * sum_{i = 1}^{n}dis_{lca(i, u)}$。
前两个东西很好维护,我们考虑如何维护后面这个$sum$,对于每一个点我们可以把它到根跳一跳,然后把这个点对答案的贡献加到线段树中,如果再限定一个$[l, r]$的区间,只要把所有年龄排序从小到大排序按照贡献加到主席树中就可以了。
注意一条树链的贡献和线段树上区间的边界要搞清楚。
主席树标记永久化一下比较好,虽然感觉空间是开不下的,但是这题就这么过去了。
时间复杂度$O((n + q)log^2n)$。
Code:
#include <cstdio> #include <cstring> #include <algorithm> using namespace std; typedef long long ll; const int N = 1.5e5 + 5; const int M = 1e7 + 100; const int inf = 1 << 30; int n, qn, tot = 0, head[N], dfsc = 0, dep[N]; int top[N], fa[N], siz[N], son[N], id[N]; ll P, dis[N], toVal[N], sumE[N], sumDis[N]; struct Edge { int to, nxt; ll val; } e[N << 1]; inline void add(int from, int to, ll val) { e[++tot].to = to; e[tot].val = val; e[tot].nxt = head[from]; head[from] = tot; } struct Item { int age, id; friend bool operator < (const Item &x, const Item &y) { if(x.age == y.age) return x.id < y.id; else return x.age < y.age; } } a[N]; template <typename T> inline void read(T &X) { X = 0; char ch = 0; T 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; } inline void swap(int &x, int &y) { int t = x; x = y; y = t; } inline int max(int x, int y) { return x > y ? x : y; } inline int min(int x, int y) { return x > y ? y : x; } void dfs1(int x, int fat, int depth, ll nowDis) { fa[x] = fat, dep[x] = depth; siz[x] = 1, dis[x] = nowDis; int maxson = -1; for(int i = head[x]; i; i = e[i].nxt) { int y = e[i].to; if(y == fat) continue; dfs1(y, x, depth + 1, nowDis + e[i].val); toVal[y] = e[i].val; siz[x] += siz[y]; if(siz[y] > maxson) { maxson = siz[y]; son[x] = y; } } } void dfs2(int x, int topf) { top[x] = topf, sumE[id[x] = ++dfsc] = toVal[x]; if(!son[x]) return; dfs2(son[x], topf); for(int i = head[x]; i; i = e[i].nxt) { int y = e[i].to; if(y == fa[x] || y == son[x]) continue; dfs2(y, y); } } namespace PSegT { struct Node { int lc, rc; ll sum, cnt; } s[M]; int root[N], nodeCnt = 0; #define lc(p) s[p].lc #define rc(p) s[p].rc #define sum(p) s[p].sum #define cnt(p) s[p].cnt #define mid ((l + r) >> 1) void ins(int &p, int l, int r, int x, int y, int pre) { s[p = ++nodeCnt] = s[pre]; if(x <= l && y >= r) { ++cnt(p); return; } sum(p) += sumE[min(y, r)] - sumE[max(x, l) - 1]; if(x <= mid) ins(lc(p), l, mid, x, y, lc(pre)); if(y > mid) ins(rc(p), mid + 1, r, x, y, rc(pre)); } ll query(int p, int l, int r, int x, int y) { ll res = 1LL * cnt(p) * (sumE[min(y, r)] - sumE[max(x, l) - 1]); if(x <= l && y >= r) return res + sum(p); if(x <= mid) res += query(lc(p), l, mid, x, y); if(y > mid) res += query(rc(p), mid + 1, r, x, y); return res; } } using namespace PSegT; inline void modify(int rt, int x) { for(; x; x = fa[top[x]]) ins(root[rt], 1, n, id[top[x]], id[x], root[rt]); } inline ll solve(int rt, int x) { ll res = 0LL; for(; x; x = fa[top[x]]) res += query(root[rt], 1, n, id[top[x]], id[x]); return res; } int main() { // freopen("Sample.txt", "r", stdin); read(n), read(qn), read(P); for(int i = 1; i <= n; i++) { read(a[i].age); a[i].id = i; } for(int i = 1; i < n; i++) { int x, y; ll v; read(x), read(y), read(v); add(x, y, v), add(y, x, v); } dfs1(1, 0, 1, 0LL), dfs2(1, 1); /* for(int i = 1; i <= n; i++) printf("%d ", top[i]); printf(" "); */ sort(a + 1, a + 1 + n); for(int i = 1; i <= n; i++) { sumE[i] += sumE[i - 1]; sumDis[i] = sumDis[i - 1] + dis[a[i].id]; } for(int i = 1; i <= n; i++) { root[i] = root[i - 1]; modify(i, a[i].id); } /* for(int i = 1; i <= n; i++) printf("%lld ", sumE[i]); printf(" "); for(int i = 1; i <= n; i++) printf("%lld ", sumDis[i]); printf(" "); */ ll ans = 0LL; for(int x, l, r; qn--; ) { read(x), read(l), read(r); l = (1LL * l + ans) % P, r = (1LL * r + ans) % P; if(l > r) swap(l, r); l = lower_bound(a + 1, a + 1 + n, (Item) {l, 0}) - a; r = upper_bound(a + 1, a + 1 + n, (Item) {r, inf}) - a - 1; ans = 1LL * (r - l + 1) * dis[x] + sumDis[r] - sumDis[l - 1] - 2LL * (solve(r, x) - solve(l - 1, x)); printf("%lld ", ans); } return 0; }