树链剖分好题。node
若是咱们暴力作的话,那么就是一一求出LCA,暴力统计。ios
优化的话,咱们须要将\(dep[LCA(i,z)]\)转化成咱们能够会利用操做。c++
以下转化:优化
\(dep[u]\)表明根节点到该结点路径上通过点的数量(包括端点),若是咱们计算\(dep[LCA(i,z)]\)那么,咱们能够从一个节点向上对于该结点到根节点路径上每一个节点进行标记(\(+1\)),另外一个节点向上遍历累加路径上的点权即为深度。ui
这样一来,就将求解深度和树上操做结合。对于多个点也同样。由于它们都有一个公共的\(z\),故其余的点向上对于路径上的点权\(+1\)便可,\(z\)统计它到根节点的路径上点权为多少。spa
打标记与求和能够用轻重链剖分来作,对于每次都将线段树清空,依次插入节点,时间复杂度为:\(O(qnlogn)\)。code
——> 优化:首先有个特色:\(i\in [l,r]\)这一段是连续的(显然),上述作法也能够知足不连续的区间(换言之,求答案的时候\(i\)能够不是连续的)。那么说明对于\([l,r]\),有其余的性质。咱们能够尝试离线\(+\)差分;blog
就是说,咱们能够把(含\(r\))\(r\)以前的全部节点依次上面讨论的那样运行,答案是当前路径点权和减去\(l-1\)以前的点权和。那么,咱们能够对于每个节点记录做用在它身上的操做是什么;接着咱们一个个插入点,离线操做,更新。get
#include<iostream> #include<cstring> #include<cstdio> #include<vector> #include<cmath> #define Re register #define wht 1,1,n #define lson p<<1,l,mid #define rson p<<1|1,mid+1,r #define FOR(i, x, y) for(Re int i=x;i<=y;++i) #define CLR(x, y) memset(x,y,sizeof(x)) using namespace std; const int SIZE = 5e4 + 5, mod = 201314; typedef long long LL; struct node { int id, flag, z; }; struct Segment { LL sum, flag; } T[SIZE << 2]; Segment merge(Segment x, Segment y) { Segment res; res.flag = 0, res.sum = (x.sum + y.sum) % mod; return res; } vector <int> G[SIZE]; vector <node> res[SIZE]; int n, q; LL ans[SIZE]; int idx = 0, dfn[SIZE], rnk[SIZE], top[SIZE], dep[SIZE], siz[SIZE], fa[SIZE], son[SIZE]; void spread(int p, int l, int r) { if(T[p].flag && l < r) { int mid = l + ((r - l) >> 1); T[p << 1].flag = (T[p << 1].flag + T[p].flag) % mod; T[p << 1 | 1].flag = (T[p << 1 | 1].flag + T[p].flag) % mod; T[p << 1].sum = (T[p << 1].sum + 1ll * (mid - l + 1) * T[p].flag % mod) % mod; T[p << 1 | 1].sum = (T[p << 1 | 1].sum + 1ll * (r - mid) * T[p].flag % mod) % mod; T[p].flag = 0; } return; } void dfs1(int u, int Fa) { fa[u] = Fa, dep[u] = dep[Fa] + 1, siz[u] = 1; son[u] = -1; int v; for(int i = 0; i < G[u].size(); ++ i) { v = G[u][i]; if(v == Fa) continue; dfs1(v, u); siz[u] += siz[v]; if(son[u] == -1 || siz[v] > siz[son[u]]) son[u] = v; } return; } void dfs2(int u, int Top) { dfn[u] = ++ idx, top[u] = Top; rnk[idx] = u; int v; if(son[u] != -1) dfs2(son[u], Top); for(int i = 0; i < G[u].size(); ++ i) { v = G[u][i]; if(v != fa[u] && v != son[u]) dfs2(v, v); } return; } void build(int p, int l, int r) { if(l == r) { T[p].flag = T[p].sum = 0; return; } int mid = l + ((r - l) >> 1); build(lson), build(rson); } void modify(int p, int l, int r, int L, int R, int val) { if(l >= L && r <= R) { T[p].sum = (T[p].sum + 1ll * (r - l + 1) * val % mod) % mod; T[p].flag = (T[p].flag + val) % mod; return; } spread(p, l, r); int mid = l + ((r - l) >> 1); if(L <= mid) modify(lson, L, R, val); if(R > mid) modify(rson, L, R, val); T[p] = merge(T[p << 1], T[p << 1 | 1]); return; } LL query(int p, int l, int r, int L, int R) { if(l >= L && r <= R) return T[p].sum; spread(p, l, r); int mid = l + ((r - l) >> 1); LL res = 0; if(L <= mid) res = query(lson, L, R); if(R > mid) res = (res + query(rson, L, R)) % mod; return res; } void modify(int u) { while(top[u] != 1) { modify(wht, dfn[top[u]], dfn[u], 1); u = fa[top[u]]; } modify(wht, dfn[1], dfn[u], 1); return; } LL query(int u) { LL res = 0; while(top[u] != 1) { res = (res + query(wht, dfn[top[u]], dfn[u])) % mod; u = fa[top[u]]; } res = (res + query(wht, dfn[1], dfn[u])) % mod; return res; } int main() { scanf("%d %d", &n, &q); int v, l, r, z; FOR(i, 2, n) { scanf("%d", &v);//因为节点编号并不是从1开始的,小心! G[i].push_back(v + 1); G[v + 1].push_back(i); } dfs1(1, 0), dfs2(1, 1); build(wht); FOR(i, 1, n) res[i].clear(); FOR(i, 1, q) { scanf("%d %d %d", &l, &r, &z); ++ r, ++ z; res[l].push_back((node){i, -1, z}); res[r].push_back((node){i, 1, z}); } CLR(ans, 0); FOR(i, 1, n) { modify(i); for(int j = 0; j < res[i].size(); ++ j) { node now = res[i][j]; ans[now.id] = (ans[now.id] + now.flag * query(now.z) % mod) % mod; } } FOR(i, 1, q) printf("%lld\n", (ans[i] % mod + mod) % mod); return 0; }