Description
给你一棵带边权、带点权的树。
每次询问所有点权为l~r的节点到节点u的距离之和
Sample Input
10 10 10
0 0 7 2 1 4 7 7 7 9
1 2 270
2 3 217
1 4 326
2 5 361
4 6 116
3 7 38
1 8 800
6 9 210
7 10 278
8 9 8
2 8 0
9 3 1
8 0 8
4 2 7
9 7 3
4 7 0
2 2 7
3 2 1
2 3 4
Sample Output
1603
957
7161
9466
3232
5223
1879
1669
1282
0
这道题真是太巧妙了。。。
我们把模型简化一下变成所有节点到节点u的距离,然后可以想到树上两点距离公式:
dep(x,y)=dep(1,x)+dep(1,y)-dep(lca)*2。
这个dep(1,x)+dep(1,y)这一部分我们其实很容易可以求出来,然后思考dep(lca)这一段,然后可以想到 [LNOI2014]LCA那道题。我们把树上每个点到根节点的节点全部+1,那么树上每个节点跟u的LCA的距离和就是:节点u到根节点的点的点权*这个点的距离,这个东西我们考虑树链剖分来维护。好吧,接下来就是怎么求一段点权的情况,这个很容易想到主席树,那你就排一下序,离散化一下,按顺序插链就好。
这里要注意一个点,可持久化线段树的lazy是不下传的,因为在可持久化线段树中有很多公用的节点,如果你在当前query的时态里下传了标记,那么其他时态与这个时态公用的节点也会受到影响,那其他时态就会多出当前query时态的影响,所以就不可以下传的。
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long LL;
const int N = 160000;
struct trnode {
int lc, rc;
LL c, lazy;
} t[75 * N]; int cnt, rt[N];
struct edge {
int x, y, d, next;
} e[2 * N]; int len, last[N];
struct node {
int x, id;
} a[N];
int n, id, dep[N], tot[N], son[N], fa[N], ys[N], top[N];
LL sum[N], num[N], s1[N], s2[N], dis[N];
int cl[N];
bool cmp(node aa, node bb) {return aa.x < bb.x;}
void ins(int x, int y, int d) {
e[++len].x = x; e[len].y = y; e[len].d = d;
e[len].next = last[x]; last[x] = len;
}
void Link(int &u, int l, int r, int ll, int rr) {
if(!u) u = ++cnt;
t[u].c += sum[rr] - sum[ll - 1];
if(l == ll && r == rr) {t[u].lazy++; return;}
int mid = (l + r) / 2;
if(rr <= mid) Link(t[u].lc, l, mid, ll, rr);
else if(ll > mid) Link(t[u].rc, mid + 1, r, ll, rr);
else Link(t[u].lc, l, mid, ll, mid), Link(t[u].rc, mid + 1, r, mid + 1, rr);
}
void Merge(int &u1, int u2) {
if(!u1 || !u2) {u1 = u1 + u2; return;}
t[u1].c += t[u2].c; t[u1].lazy += t[u2].lazy;
Merge(t[u1].lc, t[u2].lc);
Merge(t[u1].rc, t[u2].rc);
}
LL findsum(int u1, int u2, int l, int r, int ll, int rr, LL lazy) {
if(l == ll && r == rr) return t[u2].c - t[u1].c + (sum[r] - sum[l - 1]) * lazy;
int mid = (l + r) / 2; LL o = t[u2].lazy - t[u1].lazy;
if(rr <= mid) return findsum(t[u1].lc, t[u2].lc, l, mid, ll, rr, lazy + o);
else if(ll > mid) return findsum(t[u1].rc, t[u2].rc, mid + 1, r, ll, rr, lazy + o);
else return findsum(t[u1].lc, t[u2].lc, l, mid, ll, mid, lazy + o)
+ findsum(t[u1].rc, t[u2].rc, mid + 1, r, mid + 1, rr, lazy + o);
}
void pre_tree_node(int x) {
tot[x] = 1; son[x] = 0;
for(int k = last[x]; k; k = e[k].next) {
int y = e[k].y;
if(y != fa[x]) {
fa[y] = x;
dep[y] = dep[x] + 1;
pre_tree_node(y);
tot[x] += tot[y];
if(tot[son[x]] < tot[y]) son[x] = y;
}
}
}
void pre_tree_edge(int x, int tp, LL dd, int d) {
ys[x] = ++id; top[x] = tp;
num[id] = d; dis[x] = dd;
s2[cl[x]] += dis[x];
int gg;
for(int k = last[x]; k; k = e[k].next) {
int y = e[k].y;
if(son[x] == y) gg = e[k].d;
}
if(son[x]) pre_tree_edge(son[x], tp, gg + dd, gg);
for(int k = last[x]; k; k = e[k].next) {
int y = e[k].y;
if(fa[x] != y && son[x] != y) {
pre_tree_edge(y, y, dd + e[k].d, e[k].d);
}
}
}
void change(int x, int y, int now) {
int tx = top[x], ty = top[y];
while(tx != ty) {
if(dep[tx] > dep[ty]) swap(tx, ty), swap(x, y);
Link(now, 1, n, ys[ty], ys[y]);
y = fa[ty]; ty = top[y];
}
if(x == y) return;
if(dep[x] > dep[y]) swap(x, y);
Link(now, 1, n, ys[son[x]], ys[y]);
}
int erfen1(int x) {
int l = 1, r = n, ans = 0;
while(l <= r) {
int mid = (l + r) / 2;
if(a[mid].x <= x) l = mid + 1, ans = mid;
else r = mid - 1;
} return cl[a[ans].id];
}
int erfen2(int x) {
int l = 1, r = n, ans = n + 1;
while(l <= r) {
int mid = (l + r) / 2;
if(a[mid].x >= x) r = mid - 1, ans = mid;
else l = mid + 1;
} return cl[a[ans].id];
}
LL solve(int u, int x, int y) {
x = erfen2(x), y = erfen1(y);
if(x > y) return 0;
int xx = x, yy = y; LL ans = 0;
int rtx = rt[x - 1], rty = rt[y];
x = 1; y = u; int tx = top[x], ty = top[y];
while(tx != ty) {
if(dep[tx] > dep[ty]) swap(tx, ty), swap(x, y);
ans += findsum(rtx, rty, 1, n, ys[ty], ys[y], 0);
y = fa[ty]; ty = top[y];
}
if(x == y) return (s1[yy] - s1[xx - 1]) * dis[u] + s2[yy] - s2[xx - 1] - 2LL * ans;
if(dep[x] > dep[y]) swap(x, y);
ans += findsum(rtx, rty, 1, n, ys[x], ys[y], 0);
return (s1[yy] - s1[xx - 1]) * dis[u] + s2[yy] - s2[xx - 1] - 2LL * ans;
}
int main() {
int m; LL A; scanf("%d%d%lld", &n, &m, &A);
for(int i = 1; i <= n; ++i) scanf("%d", &a[i].x), a[i].id = i;
sort(a + 1, a + n + 1, cmp);
int kk = 0; a[0].x = -1;
for(int i = 1; i <= n; ++i) {
if(a[i].x != a[i - 1].x) kk++;
cl[a[i].id] = kk;
}
for(int i = 1; i < n; ++i) {
int x, y, d; scanf("%d%d%d", &x, &y, &d);
ins(x, y, d), ins(y, x, d);
}
for(int i = 1; i <= n; ++i) s1[cl[i]]++;
pre_tree_node(1);
pre_tree_edge(1, 1, 0, 0);
for(int i = 1; i <= n; ++i) sum[i] = sum[i - 1] + (LL)num[i], s1[i] += s1[i - 1], s2[i] += s2[i - 1];
kk = 1; a[0].id = 0, a[n + 1].id = n + 1; cl[0] = 0, cl[n + 1] = kk + 1;
for(int i = 1; i <= n; ++i) {
rt[kk] = ++cnt;
change(1, a[i].id, rt[kk]);
while(i < n && a[i].x == a[i + 1].x) change(1, a[++i].id, rt[kk]);
Merge(rt[kk], rt[kk - 1]);
kk++;
}
LL lastans = 0;
for(int i = 1; i <= m; ++i) {
int x; LL a, b; scanf("%d%lld%lld", &x, &a, &b);
int l = (a + lastans) % A, r = (b + lastans) % A;
if(l > r) swap(l, r); lastans = solve(x, l, r);
printf("%lld\n", lastans);
}
return 0;
}