BZOJ 3626: [LNOI2014]LCA

3626: [LNOI2014]LCA

Time Limit: 10 Sec  Memory Limit: 128 MB
Submit: 3810  Solved: 1520
[Submit][Status][Discuss]

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

总结:这道题感觉比较神
如果暴力上lca,复杂度很明显是O(n * q * logn)T了
正解不需要求lca
考虑如果将z到根节点的路径染色,则(l, r)区间的点与z的lca必在这条路径上
于是可将题目转化为,将z到根节点路径上的点的点权赋为1,则(l, r)区间的点到根节点的点权和即为答案
然后又可以转化为将(l, r)区间的点到根节点的路径所经过的点权值赋为1,求z到根节点的点权和
考虑差分,每次答案相当于query(1, r) - query(1, l - 1)
于是可以离线做,一次1....n加点
注意要取模,根节点为0
#include<bits/stdc++.h>

using namespace std;
const int maxn = 200005;
const int N = 50005;

int laz[maxn], sum[maxn], dep[N], f[N], tot, cnt = 1;
int id[N], siz[N], son[N], top[N], n, m, head[maxn];

struct Node{
	int v, nxt; 
} G[maxn];
struct data {
	int p, v, id, z;
} a[maxn];
void ins(int u, int v) {
	G[cnt] = (Node) {v, head[u]}; head[u] = cnt++;
}

int ans[N];
void dfs1(int x, int fa, int deep) {
	dep[x] = deep; siz[x] = 1;
	int maxson = -1;
	for (int i = head[x]; i; i = G[i].nxt) {
		int v = G[i].v;
		if(v == fa) continue;
		dfs1(v, x, deep + 1);
		siz[x] += siz[v];
		if(siz[v] > maxson) maxson = siz[v], son[x] = v;
	}
}
void dfs2(int x, int topf) {
	top[x] = topf; id[x] = ++tot;
	if(son[x] != 0) dfs2(son[x], topf);
	for (int i = head[x]; i; i = G[i].nxt) {
		int v = G[i].v;
		if(v == f[x] || v == son[x]) continue;
		dfs2(v, v);
	} 
}
void pushdown(int o, int l, int r) {
	if(laz[o] != 0) {
		int mid = (l + r) >> 1;
		laz[o << 1] += laz[o];
		laz[o << 1 | 1] += laz[o];
		sum[o << 1] += (mid - l + 1) * laz[o];
		sum[o << 1 | 1] += (r - mid) * laz[o];
		laz[o] = 0;
	}
}
void update(int o) {
	sum[o] = sum[o << 1] + sum[o << 1 | 1];
}
void modify(int o, int l, int r, int ql, int qr, int z) {
	if(ql <= l && r <= qr) {
		sum[o] += (r - l + 1); laz[o] += z;
		return;
	} int mid = (l + r) >> 1;
	pushdown(o, l, r);
	if(ql <= mid) modify(o << 1, l, mid, ql, qr, z);
	if(qr > mid) modify(o << 1 | 1, mid + 1, r, ql, qr, z);
	update(o);
}
void modify_x(int x) {
	while(top[x]) {
		modify(1, 1, n, id[top[x]], id[x], 1); x = f[top[x]];
	} modify(1, 1, n, 1, id[x], 1);
}
int query(int o, int l, int r, int ql, int qr) {
	int res = 0;
	if(ql <= l && r <= qr) return sum[o];
	int mid = (l + r) >> 1;
	pushdown(o, l, r);
	if(ql <= mid) res += query(o << 1, l, mid, ql, qr);
	if(qr > mid) res += query(o << 1 | 1, mid + 1, r, ql, qr);
	return res;
}
int query_sum(int x) {
	int res = 0;
	while(top[x]) {
		res += query(1, 1, n, id[top[x]], id[x]); x = f[top[x]];
	} res += query(1, 1, n, 1, id[x]);
	return res;
}
int cmp(data a, data b) {
	return a.p < b.p;
} 

int main() {
	scanf("%d%d", &n, &m);
	for (int i = 1; i <= n - 1; ++i) {
		scanf("%d", &f[i]); ins(f[i], i); ins(i, f[i]);
	} dfs1(0, -1, 0); dfs2(0, 0); 
	for (int i = 1; i <= m; ++i) {
		int l, r, z;
		scanf("%d%d%d", &l, &r, &z);
		a[i] = (data) {l - 1, -1, i, z};
		a[i + m] = (data) {r, 1, i, z};
	} 
	sort(a + 1, a + m * 2 + 1, cmp); int h = 0;
	for (int i = 1; i <= 2 * m; ++i) {
		while(h <= a[i].p) modify_x(h++);
		ans[a[i].id] += a[i].v * query_sum(a[i].z); 
	} 
	for (int i = 1; i <= m; ++i) printf("%d ", ans[i]);
	return 0;
}

  

猜你喜欢

转载自www.cnblogs.com/oi-forever/p/9071092.html
今日推荐