bzoj3626: [LNOI2014]LCA 树链剖分
2018-03-06 20:12
399 查看
bzoj3626: [LNOI2014]LCA
Description
给出一个n个节点的有根树(编号为0到n-1,根节点为0)。一个点的深度定义为这个节点到根的距离+1。设dep[i]表示点i的深度,LCA(i,j)表示i与j的最近公共祖先。
有q次询问,每次询问给出l r z,求sigma_{l<=i<=r}dep[LCA(i,z)]。
(即,求在[l,r]区间内的每个节点i与z的最近公共祖先的深度之和)
Input
第一行2个整数n q。接下来n-1行,分别表示点1到点n-1的父节点编号。
接下来q行,每行3个整数l r z。
Output
输出q行,每行表示一个询问的答案。每个答案对201314取模输出Sample Input
5 20
0
1
1
1 4 3
1 4 2
Sample Output
85
HINT
共5组数据,n与q的规模分别为10000,20000,30000,40000,50000。分析
一道很好的树链剖分的题目对于一个询问,考虑每个区间内的点对答案的贡献。
考虑仅仅统计两个点的答案。
我们暴力爬树。
当爬到公共点lca之后,每往上爬一次答案加一,一直爬到根结束,得出来的就是这两点的答案。
每个点都是如此。
考虑优化上述过程。
插入一个点之后把这个点到根的每个点权值加一。
最后统计询问点到根路径上每个点权值和就是答案。
对于一个询问显然是可以用树剖优化的。
对于多个询问,考虑离线。我们将一个询问[l,r]分解成询问[1,l-1]和[1,r]
然后排序插点即可。
代码
/************************************************************** Problem: 3626 User: 2014lvzelong Language: C++ Result: Accepted Time:932 ms Memory:5228 kb ****************************************************************/ #include<iostream> #include<cstdlib> #include<cstdio> #include<cstring> #include<algorithm> #include<map> #include<cmath> using namespace std; int read() { char ch = getchar(); int x = 0, f = 1; for(;ch < '0' || ch > '9'; ch = getchar()) if(ch == '-') f = -1; for(;ch >= '0' && ch <= '9'; ch = getchar()) x = (x << 1) + (x << 3) - '0' + ch; return x * f; } const int N = 5e4 + 10, P = 201314; int de , ds , d , f , siz , in , ans , n; int to , pre , nxt , T , D , top, tot; struct data { int x, y, id, f; bool operator < (data a) const {return x < a.x;} }a[N << 1]; void add(int u, int v) {to[++top] = v; nxt[top] = pre[u]; pre[u] = top;} void dfs1(int u, int fa) { de[u] = de[fa] + 1; ds[u] = 0; siz[u] = 1; for(int i = pre[u]; i; i = nxt[i]) { dfs1(to[i], u); siz[u] += siz[to[i]]; if(siz[ds[u]] < siz[to[i]]) ds[u] = to[i]; } } void dfs2(int u, int chain) { in[u] = ++tot; d[u] = chain; if(!ds[u]) return; dfs2(ds[u], chain); for(int i = pre[u]; i; i = nxt[i]) if(to[i] != ds[u]) dfs2(to[i], to[i]); } int SA(int *a, int x, int ad) {for(; x <= n; x += x & -x) a[x] = (a[x] + ad) % P;} int SQ(int *a, int x) {int ret = 0; for(; x; x -= x & -x) ret = (ret + a[x]) % P; return ret;} void Ins(int L, int R) { SA(T, L, 1); SA(T, R + 1, -1); SA(D, L, L); SA(D, R + 1, -R - 1); } int Que(int x) {return ((x + 1) * SQ(T, x) % P - SQ(D, x)) % P;} int Que(int L, int R) {return (Que(R) - Que(L - 1)) % P;} void Change(int u) { for(;d[u] != 1; u = f[d[u]]) Ins(in[d[u]], in[u]); Ins(1, in[u]); } int Query(int u) { int ans = 0; for(;d[u] != 1; u = f[d[u]]) ans += Que(in[d[u]], in[u]); return ans + Que(1, in[u]); } int main() { n = read(); int m = read(); for(int i = 2;i <= n; ++i) add(f[i] = read() + 1, i); for(int i = 1;i <= m; ++i) { int l = read(), r = read(), z = read() + 1; a[++tot].x = l; a[tot].id = i; a[tot].y = z; a[tot].f = -1; a[++tot].x = r + 1; a[tot].id = i; a[tot].y = z; a[tot].f = 1; } sort(a + 1, a + tot + 1); tot = 0; dfs1(1, 0); dfs2(1, 1); tot = m << 1; int j = 1; for(int i = 1;i <= n; ++i) { for(;j <= tot && a[j].x < i; ++j) ans[a[j].id] = (ans[a[j].id] + Query(a[j].y) * a[j].f % P) % P; if(j > tot) break; Change(i); } for(;j <= tot; ++j) ans[a[j].id] = (ans[a[j].id] + Query(a[j].y) * a[j].f % P) % P; for(int i = 1;i <= m; ++i) printf("%d\n", (ans[i] + P) % P); return 0; }
相关文章推荐
- bzoj 3626: [LNOI2014]LCA 离线+树链剖分
- BZOJ 3626: [LNOI2014]LCA [树链剖分 离线|主席树]
- [bzoj3626][LNOI2014]LCA 树链剖分
- BZOJ[3626][LNOI2014]LCA 树链剖分+线段树
- BZOJ3626 [LNOI2014]LCA(树链剖分)
- BZOJ 3626: [LNOI2014]LCA 树链剖分 询问差分
- bzoj 3626 [LNOI2014]LCA(离线处理+树链剖分,线段树)
- 【BZOJ3626】[LNOI2014]LCA 离线+树链剖分+线段树
- [BZOJ 3626] [LNOI2014] LCA 【树链剖分 + 离线 + 差分询问】
- BZOJ 3626 [LNOI2014]LCA - 树链剖分+线段树结构+玄学离散
- bzoj 3626: [LNOI2014]LCA 树链剖分
- [BZOJ3626][LNOI2014][树链剖分][差分][离线处理]LCA
- BZOJ 3626 [LNOI2014]LCA ——树链剖分
- BZOJ 3626: [LNOI2014]LCA( 树链剖分 + 离线 )
- 【bzoj3626】[LNOI2014]LCA 树链剖分+线段树
- BZOJ 3626 [LNOI2014]LCA 树链剖分 离线+差分
- bzoj 3626: [LNOI2014]LCA 树链剖分
- 【树链剖分】【线段树】bzoj3626 [LNOI2014]LCA
- bzoj3626 [LNOI2014]LCA
- bzoj 3626: [LNOI2014]LCA