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 2
0
0
1
1
1 4 3
1 4 2
0
0
1
1
1 4 3
1 4 2
Sample Output
8
5
5
HINT
共5组数据,n与q的规模分别为10000,20000,30000,40000,50000。
Source
题解:
考虑暴力,对与询问l,r,z。
把z到根的路径提出来,路径上的每一个点u(u不为树的根),以fa[u]为根的子树去掉以u为根的子树后,子树里点编号在[l,r]的个数*这个点的深度就是这个点的贡献。
所有的贡献和就是答案。
这看上去很像一个二维数点问题。其实这个叫做树上数点问题。
做法和二维数点一样,先离线排序询问,然后容斥按照区间端点来排序,用树链剖分套线段树来维护这个东西。
代码:
#include <cstdio> #include <cstring> #include <algorithm> #include <cstdlib> #include <ctime> #include <iostream> const int mod = 201314; struct edge { int to; edge *next; } e[100010], *et = e, *last[50010]; void adde(int u, int v) { *++et = (edge) {v, last[u]}, last[u] = et; } int dep[50010], fa[50010], siz[50010], son[50010]; void dfs1(int u) { dep[u] = dep[fa[u]]+1, siz[u] = 1; for(edge *it = last[u]; it; it = it->next) if(fa[u] != it->to) { fa[it->to] = u; dfs1(it->to); siz[u] += siz[it->to]; if(siz[son[u]] < siz[it->to]) son[u] = it->to; } } int top[50010], dfn[50010], ck; void dfs2(int u, int tp) { top[u] = tp; dfn[u] = ++ck; if(son[u]) dfs2(son[u], tp); for(edge *it = last[u]; it; it = it->next) if(fa[u] != it->to && son[u] != it->to) dfs2(it->to, it->to); } struct info { int l, r; int sum, add; } t[200010]; info unit(info a, info b) { info x; x.l = a.l, x.r = b.r; x.sum = a.sum+b.sum, x.add = 0; x.sum %= mod; return x; } void push_up(int o) { t[o] = unit(t[o<<1], t[o<<1|1]); } void add(int o, int v) { t[o].add += v; t[o].sum += 1ll*v*(t[o].r-t[o].l+1)%mod; t[o].sum %= mod; } void push_down(int o) { if(t[o].add) { add(o<<1, t[o].add); add(o<<1|1, t[o].add); t[o].add = 0; } } void build(int o, int l, int r) { t[o].l = l, t[o].r = r; if(l == r) return; int mid = (l+r)>>1; build(o<<1, l, mid); build(o<<1|1, mid+1, r); } int query(int o, int l, int r) { if(r < t[o].l || t[o].r < l) return 0; if(l <= t[o].l && t[o].r <= r) return t[o].sum; push_down(o); return (query(o<<1, l, r)+query(o<<1|1, l, r))%mod; } void modify(int o, int l, int r) { if(r < t[o].l || t[o].r < l) return; if(l <= t[o].l && t[o].r <= r) return add(o, 1); push_down(o); modify(o<<1, l, r); modify(o<<1|1, l, r); push_up(o); } void modify(int x) { while(x) modify(1, dfn[top[x]], dfn[x]), x = fa[top[x]]; return; } int query(int x) { int ans = 0; while(x) { ans += query(1, dfn[top[x]], dfn[x]), ans %= mod, x = fa[top[x]]; } return ans; } int n, q; int ans[50010]; struct ask { int r, x, type, id; bool operator < (const ask &that) const { return r < that.r; } } qq[100010]; int main() { scanf("%d%d", &n, &q); for(int i = 2; i <= n; i++) { int x; scanf("%d", &x); x++; adde(x, i); } dfs1(1), dfs2(1, 1); build(1, 1, n); for(int i = 1; i <= q; i++) { scanf("%d%d%d", &qq[i*2-1].r, &qq[i*2].r, &qq[i*2-1].x); qq[i*2-1].x++; qq[i*2].x = qq[i*2-1].x; qq[i*2].r++; qq[i*2].type = 1; qq[i*2-1].id = qq[i*2].id = i; } std::sort(qq+1, qq+2*q+1); int k = 1; for(int i = 0; i <= n; i++) { if(i) modify(i); while(k <= 2*q && qq[k].r == i) { if(qq[k].type) ans[qq[k].id] += query(qq[k].x); else ans[qq[k].id] -= query(qq[k].x); k++; //printf("k = %d\n", k); } } //printf("q2 = %d\n", query(2)); for(int i = 1; i <= q; i++) ans[i] %= mod, ans[i] < 0 ? ans[i] += mod : 1, printf("%d\n", ans[i]); return 0; }