BZOJ 3626 LNOI 2014 LCA 树链剖分
2016-04-07 23:22
323 查看
q个询问统计编号[l,r]内的点与某个点z的LCA的深度和。
任何点与z的LCA都在z到根的路径上,若某个需要统计的点在z的某个祖先的另外的子树中,那么该点对答案的贡献值为该祖先的深度。。
要统计的点在z的子树中:
ans=dep[z]×sz[z]
要统计的点不在z的子树中:
ans=∑i is the ancestor of zdep[fa[i]]×(sz[fa[i]]−sz[i])
求[l,r]可以转化为求[0,r]-[0,l)。
扫描区间右端点,转移:维护新加入的节点的祖先,即祖先的sz全部+1,发现是区间修改,可以考虑树链剖分解决。
那么对于某时候的区间,我们可以快速查询某个点的子树大小,至此我们可以在O(logn)的时间内计算一个点的贡献值。重链上的点呢?分析一下性质,发现查询链上的重链上除深度最大的点外,其他的我们需要统计的都必在其轻儿子里。因此我们可以在加点的时候同时维护其到根链上所有相应的点。即不断跳重链。
在查询链上的重链的深度最大的点,有我们询问的节点在其的轻儿子,并不能统计,而其他的更浅的节点,其重儿子在查询链上,那么轻儿子就是其他的子树啦。
因此重链允许区间查询
单次询问复杂度O(log2n)
总复杂度O((n+q)log2n)
1A好棒。Rank 16。。。
设dep[i]表示点i的深度,LCA(i,j)表示i与j的最近公共祖先。
有q次询问,每次询问给出l r z,求sigma_{l<=i<=r}dep[LCA(i,z)]。
(即,求在[l,r]区间内的每个节点i与z的最近公共祖先的深度之和)
接下来n-1行,分别表示点1到点n-1的父节点编号。
接下来q行,每行3个整数l r z。
任何点与z的LCA都在z到根的路径上,若某个需要统计的点在z的某个祖先的另外的子树中,那么该点对答案的贡献值为该祖先的深度。。
要统计的点在z的子树中:
ans=dep[z]×sz[z]
要统计的点不在z的子树中:
ans=∑i is the ancestor of zdep[fa[i]]×(sz[fa[i]]−sz[i])
求[l,r]可以转化为求[0,r]-[0,l)。
扫描区间右端点,转移:维护新加入的节点的祖先,即祖先的sz全部+1,发现是区间修改,可以考虑树链剖分解决。
那么对于某时候的区间,我们可以快速查询某个点的子树大小,至此我们可以在O(logn)的时间内计算一个点的贡献值。重链上的点呢?分析一下性质,发现查询链上的重链上除深度最大的点外,其他的我们需要统计的都必在其轻儿子里。因此我们可以在加点的时候同时维护其到根链上所有相应的点。即不断跳重链。
在查询链上的重链的深度最大的点,有我们询问的节点在其的轻儿子,并不能统计,而其他的更浅的节点,其重儿子在查询链上,那么轻儿子就是其他的子树啦。
因此重链允许区间查询
单次询问复杂度O(log2n)
总复杂度O((n+q)log2n)
1A好棒。Rank 16。。。
#include <cstdio> #include <algorithm> using namespace std; const int mod = 201314, N = 50005, M = N * 2; struct BIT { int c , n, module; void add(int i, int x) { for (; i <= n; i += i & -i) { c[i] += x; if (module) c[i] %= mod; } } void add(int l, int r, int x) { add(l, x); add(r + 1, -x); } int get(int i) { int s = 0; for (; i; i -= i & -i) { s += c[i]; if (module) s %= mod; } return s; } int sum(int i, int j) { int ans = get(j) - get(i - 1); if (module) ans = (ans % mod + mod) % mod; return ans; } } t1, t2; struct Query { int d, p, i, flag; friend bool operator< (const Query &a, const Query &b) { return a.d < b.d; } } q[N * 2]; int sz , h , p[M], v[M], ans , cnt = 0, id = 0, fa , dep , son , pos , top ; void add(int a, int b) { p[++cnt] = h[a]; v[cnt] = b; h[a] = cnt; } void dfs(int x, int f) { sz[x] = 1; fa[x] = f; dep[x] = dep[f] + 1; son[x] = 0; for (int i = h[x]; i; i = p[i]) if (v[i] != f) { dfs(v[i], x); sz[x] += sz[v[i]]; if (sz[v[i]] > sz[son[x]]) son[x] = v[i]; } } void dfs2(int x, int t) { top[x] = t; pos[x] = ++id; if (son[x]) dfs2(son[x], t); for (int i = h[x]; i; i = p[i]) if (v[i] != fa[x] && v[i] != son[x]) dfs2(v[i], v[i]); } void modify(int x) { for (; x; x = fa[top[x]]) { t1.add(pos[top[x]], pos[x], 1); t2.add(pos[x], dep[x]); } } int query(int x) { int ans = 0, last = 0; for (; x; x = fa[top[x]]) { ans = (ans + dep[x] * (t1.get(pos[x]) - last)) % mod; if (x != top[x]) ans = (ans + t2.sum(pos[top[x]], pos[fa[x]])) % mod; last = t1.get(pos[top[x]]); } return ans; } int main() { int i, j, n, nq = 0, m, a, b, c; scanf("%d%d", &n, &m); t2.module = 1; t1.n = t2.n = n; for (i = 2; i <= n; ++i) { scanf("%d", &a); add(a + 1, i); } dfs(1, 0); dfs2(1, 1); for (i = 1; i <= m; ++i) { scanf("%d%d%d", &a, &b, &c); q[++nq] = (Query) { a, c + 1, i, 0 }; q[++nq] = (Query) { b + 1, c + 1, i, 1 }; } sort(q + 1, q + nq + 1); for (i = 1; i <= nq; ++i) { if (q[i].d != q[i - 1].d) for (j = q[i - 1].d + 1; j <= q[i].d; ++j) modify(j); j = query(q[i].p); if (q[i].flag) ans[q[i].i] += j; else ans[q[i].i] -= j; } for (i = 1; i <= m; ++i) printf("%d\n", (ans[i] % mod + mod) % mod); return 0; }
3626: [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 2 0 0 1 1 1 4 3 1 4 2
Sample Output
8 5
HINT
共5组数据,n与q的规模分别为10000,20000,30000,40000,50000。相关文章推荐
- DOCTYPE声明的几种类型
- Android权限扫描工具
- 【bzoj4455】[Zjoi2016]小星星
- zcmu1138
- Java中的Runnable、Callable、Future、FutureTask的区别与示例
- 153 mysql索引优化入门
- django 1.8 日志配置
- C# 使用正则表达式去掉字符串中的数字,或者去掉字符串中的非数字
- Spring Aop
- 将电脑变成外网可访问的服务器
- 黎活明给程序员的忠告
- Spring Boot Web应用的异常处理
- Mycat 数据库分库分表中间件
- 对WinForm的App.config文件进行加密
- 【BZOJ3590】[Snoi2013]Quare【双连通分量】【状压DP】【神题】
- 问卷调查
- LeetCode *** 86. Partition List
- 一个排好序的数组,找出两数之和为m的所有组合
- 在NodeJS中使用流程控制工具Async
- HDU 5273 Dylans loves sequence 暴力递推