樹鏈剖分好題。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; }