BZOJ 3626 LCA

Description

给出一个\(n\)个节点的有根树(编号为\(0\)到n-1,根节点为\(0\))。一个点的深度定义为这个节点到根的距离\(+1\)
\(dep_{i}\)表示点\(i\)的深度,\(LCA(i,j)\)表示\(i\)\(j\)的最近公共祖先。
\(q\)次询问,每次询问给出\(l,r,z\),求\(\sum_{i=l}^{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\)

这题被czh随便秒。做法其实很简单,对于\(dep_{LCA(a,b)}\),我们把\(a\)到根的路径上的点的点权\(+1\),询问时我们就可以直接查询\(b\)到根路径上的点权和即可。所以这题就可以做了。
问题在于询问,好像可以建主席树(可能是我口胡的),但离线算法可能更好写。
首先我们知道询问满足可并性$$ans_{l,r,z} = ans_{0,r,z}-ans_{0,l-1,z}$$
所以我们可以将一个询问拆成两个,排序后按顺序修改,分开计算答案即可。(用\(l,r\)修改,\(z\)来查询)。
树链剖分和lct都可以写。

#include<algorithm>
#include<cstdio>
#include<cstdlib>
using namespace std;

typedef long long ll;
#define rhl (201314)
#define maxn (100010)
int N,Q,side[maxn],toit[maxn],next[maxn],tot,id[maxn],tree[maxn*4],inc[maxn*4];
int size[maxn],heavy[maxn],cnt,father[maxn],top[maxn],ans[maxn],key[maxn*4];
struct SCAN
{
	int pos,key,ord,sign;
	friend inline bool operator <(const SCAN &a,const SCAN &b) { return a.pos < b.pos; }
}scan[maxn*2];

inline void add(int a,int b) { next[++cnt] = side[a]; side[a] = cnt; toit[cnt] = b; }

inline void dfs(int now)
{
	size[now] = 1; heavy[now] = N;
	for (int i = side[now];i;i = next[i])
	{
		dfs(toit[i]);
		father[toit[i]] = now,size[now] += size[toit[i]];
		if (size[toit[i]] > size[heavy[now]]) heavy[now] = toit[i];
	}
}

inline void Div(int now,int Top)
{
	id[now] = ++tot; top[now] = Top;
	if (side[now]) Div(heavy[now],Top);
	for (int i = side[now];i;i = next[i])
		if (toit[i] != heavy[now]) Div(toit[i],toit[i]);
}

inline void pushdown(int now,int l,int r)
{
	if (!inc[now]) return;
	if (l < r)
	{
		int mid = (l + r) >> 1;
		tree[now<<1] += (ll)inc[now]*(mid-l+1)%rhl;
		tree[now<<1|1] += (ll)inc[now]*(r-mid)%rhl;
		if (tree[now<<1] >= rhl) tree[now<<1] -= rhl;
		if (tree[now<<1|1] >= rhl) tree[now<<1|1] -= rhl;
		inc[now << 1] += inc[now]; inc[now << 1|1] += inc[now];
		if (inc[now<<1] >= rhl) inc[now<<1] -= rhl;
		if (inc[now<<1|1] >= rhl) inc[now<<1|1] -= rhl;
	}
	inc[now] = 0;
}

inline void modify(int ql,int qr,int l,int r,int now)
{
	pushdown(now,l,r);
	if (ql <= l&&r <= qr)
	{
		tree[now] += r-l+1; inc[now]++;
		if (inc[now] >= rhl) inc[now] -= rhl;
		if (tree[now] >= rhl) tree[now] -= rhl; return;
	}
	int mid = (l + r) >> 1;
	if (qr <= mid) modify(ql,qr,l,mid,now<<1);
	else if (ql > mid) modify(ql,qr,mid+1,r,now<<1|1);
	else modify(ql,mid,l,mid,now<<1),modify(mid+1,qr,mid+1,r,now<<1|1);
	tree[now] = tree[now<<1]+tree[now<<1|1];
	if (tree[now] >= rhl) tree[now] -= rhl;
}

inline int ask(int ql,int qr,int l,int r,int now)
{
	pushdown(now,l,r);
	if (ql <= l&&r <= qr) return tree[now];
	int mid = (l + r) >> 1,ret;
	if (qr <= mid) ret = ask(ql,qr,l,mid,now<<1);
	else if (ql > mid) ret = ask(ql,qr,mid+1,r,now<<1|1);
	else ret = ask(ql,mid,l,mid,now<<1)+ask(mid+1,qr,mid+1,r,now<<1|1);
	if (ret >= rhl) ret -= rhl;
	return ret;
}

inline void insert(int now)
{
	for (;top[now];now = father[top[now]]) modify(id[top[now]],id[now],1,N,1);
	modify(id[0],id[now],1,N,1);
}

inline int query(int now)
{
	int ret = 0;
	for (;top[now];now = father[top[now]])
	{
		ret += ask(id[top[now]],id[now],1,N,1);
		if (ret >= rhl) ret -= rhl;
	}
	ret += ask(id[0],id[now],1,N,1);
	if (ret >= rhl) ret -= rhl;
	return ret;
}

int main()
{
	freopen("3626.in","r",stdin);
	freopen("3626.out","w",stdout);
	scanf("%d %d",&N,&Q);
	for (int i = 1,a;i < N;++i)	scanf("%d",&a),add(a,i);
	dfs(0); Div(0,0);
	for (int i = 1,l,r,z;i <= Q;++i)
	{
		scanf("%d %d %d",&l,&r,&z);
		scan[(i << 1)-1] = (SCAN){l-1,z,i,-1};
		scan[i << 1] = (SCAN){r,z,i,1};
	}
	sort(scan+1,scan+(Q<<1|1));
	for (int i = 1,j = -1;i <= (Q<<1);++i)
	{
		while (j < N&&j + 1 <= scan[i].pos) insert(++j);
		ans[scan[i].ord] += scan[i].sign*query(scan[i].key);
		if (ans[scan[i].ord] >= rhl) ans[scan[i].ord] -= rhl;
		if (ans[scan[i].ord] < 0) ans[scan[i].ord] += rhl;
	}
	for (int i = 1;i <= Q;++i) printf("%d\n",ans[i]);
	fclose(stdin); fclose(stdout);
	return 0;
}
posted @ 2015-05-20 16:07  lmxyy  阅读(199)  评论(0编辑  收藏  举报