[HNOI2015]開店
阿新 • • 發佈:2020-08-14
先考慮不要求區間怎麼做,即我們要查詢:
\[\sum\limits_{i = } ^ n dis_{u, i} = \sum\limits_{i = 1} ^ n dep_u + dep_i - 2 \times dep_{lca(u, i)} \]
前面這一坨 \(\sum\limits_{i = 1} ^ n dep_u + dep_i = n \times dep_u + \sum\limits_{i = 1} ^ n dep_i\) 可以很快計算出來,後面那一坨實際上要求的就是 \(\sum\limits_{i = 1} ^ n dep_{lca(u, i)}\),這實際上就是一道原題 [LNOI2014]LCA 我們繼續使用這題的套路,先將邊權下放到點權,將每個點到根路徑上所有點的權值加上這個點的點權,每次查詢一個點的 \(\sum\limits_{i = 1} ^ n dep_{lca(u, i)}\) 相當於這個點 \(u\) 到根節點路徑上的權值和,這個我們可以使用樹剖解決。再來看有查詢限制的情況,我們將每個妖怪按照年齡從小到大排序,以妖怪下標建立主席樹,利用主席樹的字首和性質就可以求出有限制的答案了。(其實本題也可當作可持久化樹剖模板來看待)
#include<bits/stdc++.h> using namespace std; #define ls t[p].l #define rs t[p].r #define mid (l + r) / 2 #define rep(i, l, r) for(int i = l; i <= r; ++i) #define Next(i, u) for(int i = h[u]; i; i = e[i].next) typedef long long ll; const int N = 150000 + 5; const int M = 15000000 + 5; struct node{ int w, p; bool operator < (const node &x) const{ return w == x.w ? p < x.p : w < x.w; } }a[N]; struct edge{ int v, next, w; }e[N << 1]; struct tree{ int l, r, tag; ll sum; }t[M]; ll ans, dep[N], ton[N]; int n, m, u, v, w, l, r, x, y, Q, A, tot, cnt, num, last; int s[N], h[N], rt[N], fa[N], dfn[N], dis[N], son[N], top[N]; int read(){ char c; int x = 0, f = 1; c = getchar(); while(c > '9' || c < '0'){ if(c == '-') f = -1; c = getchar();} while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar(); return x * f; } void add(int u, int v, int w){ e[++tot].v = v, e[tot].w = w, e[tot].next = h[u], h[u] = tot; e[++tot].v = u, e[tot].w = w, e[tot].next = h[v], h[v] = tot; } void dfs1(int u, int Fa){ int Max = -1; s[u] = 1, fa[u] = Fa; Next(i, u){ int v = e[i].v; if(v == Fa) continue; dep[v] = dep[u] + e[i].w, dfs1(v, u), s[u] += s[v]; if(s[v] > Max) Max = s[v], son[u] = v; } } void dfs2(int u, int topf){ top[u] = topf, dfn[u] = ++num, dis[num] = dep[u] - dep[fa[u]]; // dfs 序一定要在 dfs2 記錄 if(son[u]) dfs2(son[u], topf); Next(i, u){ int v = e[i].v; if(v == fa[u] || v == son[u]) continue; dfs2(v, v); } } void build(int &p, int l, int r){ p = ++cnt; if(l == r) return; build(ls, l, mid), build(rs, mid + 1, r); } void update(int &p, int l, int r, int x, int y){ if(p <= last) t[++cnt] = t[p], p = cnt; // 這樣可以節省大量空間,同一顆主席樹上不必新開節點 t[p].sum += dis[y] - dis[x - 1]; if(l >= x && r <= y){ ++t[p].tag; return;} if(y <= mid) update(ls, l, mid, x, y); else if(x > mid) update(rs, mid + 1, r, x, y); else update(ls, l, mid, x, mid), update(rs, mid + 1, r, mid + 1, y); // 標記永久化必須沿途直接更新答案,因為如果這個地方打上懶標記下次在下面修改時直接繼承左右兒子答案是有問題的 } ll query(int p, int l, int r, int x, int y){ if(!p || l >= x && r <= y) return t[p].sum; ll tmp = 1ll * t[p].tag * (dis[y] - dis[x - 1]); if(y <= mid) return tmp + query(ls, l, mid, x, y); else if(x > mid) return tmp + query(rs, mid + 1, r, x, y); else return tmp + query(ls, l, mid, x, mid) + query(rs, mid + 1, r, mid + 1, y); } void Modify(int x, int w){ while(top[x] != 1) update(rt[w], 1, n, dfn[top[x]], dfn[x]), x = fa[top[x]]; update(rt[w], 1, n, 1, dfn[x]); } ll Ask(int x, int w){ ll ans = 0; while(top[x] != 1) ans += query(rt[w], 1, n, dfn[top[x]], dfn[x]), x = fa[top[x]]; return ans + query(rt[w], 1, n, 1, dfn[x]); } int main(){ n = read(), Q = read(), A = read(); rep(i, 1, n) a[i].w = read(), a[i].p = i; sort(a + 1, a + n + 1); rep(i, 1, n - 1) u = read(), v = read(), w = read(), add(u, v, w); dfs1(1, 0), dfs2(1, 1); rep(i, 1, n) ton[i] = ton[i - 1] + dep[a[i].p]; rep(i, 1, n) dis[i] += dis[i - 1]; build(rt[0], 1, n); rep(i, 1, n) rt[i] = rt[i - 1], last = cnt, Modify(a[i].p, i); // 需要在主席樹上修改多次時只能這麼寫 while(Q--){ u = read(), x = read(), y = read(); l = min((x + ans % A) % A, (y + ans % A) % A), r = max((x + ans % A) % A, (y + ans % A) % A); x = l, y = r; r = upper_bound(a + 1, a + n + 1, (node){y, n}) - a - 1; l = lower_bound(a + 1, a + n + 1, (node){x, 0}) - a; // 這裡不能先離散化年齡再查 ans = 1ll * (r - l + 1) * dep[u] + ton[r] - ton[l - 1] - 2ll * (Ask(u, r) - Ask(u, l - 1)); // 不需要直接訪問區間資訊時直接字首和更方便 printf("%lld\n", ans); } return 0; }