[P4211][LNOI2014]LCA(树链剖分+差分)

一个节点的深度,就是这个节点到根的路径上的节点数,所以所求可转化为 l 到 r 区间内每个节点到根与 z 到根的公共路径上点数之和。修改和查询的路径一定是某节点到根,用树链剖分来维护。

但每次都从 l 到 r 添加贡献再还原肯定不可行。我们能发现不同询问区间可能有重叠,似乎可以离线做;维护的又是公共点数的和,是可减的,能用差分求出。所以最终只要按顺序把节点 i 到根的值都+1,然后遇到询问就查询一下该点到根的值之和,根据是1 -- l-1的区间还是1 -- r的区间判断是加还是减就好了。记得保存询问的数组要开2倍。

#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
const int N=50010;
const ll mod=201314;
struct edge{
	int y,next;
}data[N];
struct qry{
	int x,y,tp,id;
}b1[N*2];
struct node{
	ll x;
	int f,siz;
}tree[N*4];
int n,q,num,num1,h[N],dep[N],faz[N],siz[N],son[N],tid[N],top[N];
ll ans[N];
inline int read(){
	int x=0,f=0;
	char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
	return f?-x:x;
}
inline void addedge(int x,int y){
	data[++num].y=y;data[num].next=h[x];h[x]=num;
}
void dfs1(int u,int fa,int d){
	dep[u]=d;faz[u]=fa;siz[u]=1;son[u]=0;
	for(int i=h[u];i!=-1;i=data[i].next){
		int v=data[i].y;
		if(v!=fa){
			dfs1(v,u,d+1);
			siz[u]+=siz[v];
			if(siz[v]>siz[son[u]])son[u]=v;
		}
	}
}
void dfs2(int u,int fa,int t){
	tid[u]=++num1;top[u]=t;
	if(son[u]==0)return;
	dfs2(son[u],u,t);
	for(int i=h[u];i!=-1;i=data[i].next){
		int v=data[i].y;
		if(v!=fa&&v!=son[u])dfs2(v,u,v);
	}
}
bool cmp(qry i,qry j){
	return i.x<j.x;
}
inline void update(int p){
	tree[p].x=(tree[p<<1].x+tree[p<<1|1].x)%mod;
}
inline void new1(int p,int f){
	tree[p].x=(tree[p].x+(1ll*f*tree[p].siz)%mod)%mod;
	tree[p].f=(tree[p].f+f)%mod;
}
inline void pushdown(int p){
	if(tree[p].f){
		new1(p<<1,tree[p].f);new1(p<<1|1,tree[p].f);
		tree[p].f=0;
	}
}
void build(int p,int a,int b){
	tree[p].siz=b-a+1;tree[p].x=tree[p].f=0;
	if(a==b)return;
	int mid=(a+b)>>1;
	build(p<<1,a,mid);build(p<<1|1,mid+1,b);
}
void add(int p,int pa,int pb,int a,int b,int f){
	if(a<=pa&&pb<=b){new1(p,f);return;}
	pushdown(p);
	int mid=(pa+pb)>>1;
	if(a<=mid)add(p<<1,pa,mid,a,b,f);
	if(mid<b)add(p<<1|1,mid+1,pb,a,b,f);
	update(p);
}
ll query(int p,int pa,int pb,int a,int b){
	if(a<=pa&&pb<=b)return tree[p].x;
	pushdown(p);
	int mid=(pa+pb)>>1;
	ll q1=0,q2=0;
	if(a<=mid)q1=query(p<<1,pa,mid,a,b);
	if(mid<b)q2=query(p<<1|1,mid+1,pb,a,b);
	update(p);
	return (q1+q2)%mod;
}
inline void lca_add(int x){
	for(;x;x=faz[top[x]])add(1,1,n,tid[top[x]],tid[x],1);
}
inline ll lca_query(int x){
	ll ans1=0;
	for(;x;x=faz[top[x]])ans1=(ans1+query(1,1,n,tid[top[x]],tid[x]))%mod;
	return ans1;
}
int main(){
	n=read();q=read();
	num=0;memset(h,-1,sizeof h);
	for(int x,i=2;i<=n;i++){x=read()+1;addedge(x,i);}
	num1=0;dfs1(1,0,1);dfs2(1,0,1);
	for(int l,r,z,cnt=0,i=1;i<=q;i++){
		l=read()+1;r=read()+1;z=read()+1;
		b1[++cnt].x=l-1;b1[cnt].y=z;b1[cnt].id=i;b1[cnt].tp=0;
		b1[++cnt].x=r;b1[cnt].y=z;b1[cnt].id=i;b1[cnt].tp=1;
	}
	build(1,1,n);
	sort(b1+1,b1+q*2+1,cmp);
	for(int now=0,i=1;i<=2*q;i++){
		while(now<b1[i].x){now++;lca_add(now);}
		if(b1[i].tp==0)ans[b1[i].id]-=lca_query(b1[i].y);
		else ans[b1[i].id]+=lca_query(b1[i].y);
	}
	for(int i=1;i<=q;i++)printf("%lld\n",(ans[i]+mod)%mod);
	return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值