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

Sample Output

8
5

HINT

共5组数据,n与q的规模分别为10000,20000,30000,40000,50000。

 

Source

数据已加强 by saffah

 

题解:

考虑暴力,对与询问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;
}

  

posted @ 2017-11-22 23:55  wazwaztime  阅读(170)  评论(0编辑  收藏  举报