[LNOI2014]LCA

[LNOI2014]LCA
这道题难道不是数据结构水题吗 逃)
首先想到一个i点的 d e p dep dep相当于根节点走到i 进过的点之和,所以这个点对答案的贡献就是从这个点走到根节点。因此我们可以转换为当前节点 i i i到根节点所经过的所有节点权值++。
显然可以用线段树+树链剖分的数据结构进行优化 对于每一个询问 建一颗权值线段树 存储当前区间 s u m sum sum, s u m sum sum存储的就是 s u m sum sum
然后我们需要想到用 类似于前缀和的思想来进行优化
例如 l = 2 , r = 4 , z = 4 l=2 ,r=4 ,z=4 l=2,r=4,z=4 我们可以用前缀和的思想进行优化 计算 1 到 4 1到4 14 s u m sum sum − 1 到 1 的 s u m -1到1的sum 11sum 就可以得到答案了
想到了前缀和 想到了树剖+线段树 如果每一次查询 你都要新建两次树
那就会GG!
因此我们又想到了莫队的思想,将询问区间的 l , r l,r l,r排序(因为询问区间为 1 到 l 1到l 1l 1 到 r 1到r 1r,左区间都是一样的,所以我们可以将l,r一起排序),然后一个一个的加点 这样就可以不用每一次询问都新建一颗树了

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#define int long long 
using namespace std;
const int mod=201314,maxn=100010;
int n,q,tot,fa[maxn],cnt_q;
int first[maxn],nxt[maxn<<1],to[maxn<<1],dep[maxn<<1],cnt_id;
int id[maxn<<1],rk[maxn<<1],size[maxn<<1],son[maxn<<1],top[maxn<<1],now;
int tag[maxn<<1],sum[maxn<<1];
struct node{
	int id,pos,z,flag;
	inline bool operator <(const node &x)const{
		return pos<x.pos || (x.pos==pos && id<x.id);
	}
}ques[maxn<<4];
struct node1{
	int ans1,ans2;
}ans[maxn<<2];
void dfs1(int x,int father){
	size[x]=1;fa[x]=father;
	for(int i=first[x];i;i=nxt[i]){
		int y=to[i];if(y==father) continue;
		dep[y]=dep[x]+1;
		dfs1(y,x);size[x]+=size[y];
		if(size[son[x]]<size[y])
			son[x]=y;
	}
}
void dfs2(int x,int topo){
	id[x]=++cnt_id;rk[cnt_id]=x;top[x]=topo;
	if(son[x])	dfs2(son[x],topo);
	for(int i=first[x];i;i=nxt[i]){
		int y=to[i];if(y==fa[y] || y==son[x]) continue;
		dfs2(y,y);
	}
}
void add(int x,int y){nxt[++tot]=first[x];first[x]=tot;to[tot]=y;}
void tagg(int node,int l,int r,int ta)
{
	sum[node]=(sum[node]+(r-l+1)*ta)%mod;
	if(l<r) tag[node]=(tag[node]+ta)%mod;	
}
void push_down(int node,int l,int r){
	if(l<r && tag[node]){
		int mid=(l+r)>>1;
		tagg(node<<1,l,mid,tag[node]);
		tagg(node<<1|1,mid+1,r,tag[node]);
	}
	tag[node]=0;
}
void push_up(int node){sum[node]=(sum[node<<1]+sum[node<<1|1])%mod;}
void modify(int node,int l,int r,int ql,int qr){
	if(qr<l || ql>r) return ;
	if(ql<=l && r<=qr){
		tagg(node,l,r,1);
		return ;
	}
	push_down(node,l,r);
	int mid=(l+r)>>1;
	modify(node<<1,l,mid,ql,qr);modify(node<<1|1,mid+1,r,ql,qr);
	push_up(node);
}
void line_modify(int x,int y){
	int tx=top[x],ty=top[y];
	while(tx!=ty){
		if(dep[tx]<dep[ty]){
			x^=y^=x^=y;tx^=ty^=tx^=ty;
		}
		modify(1,1,n,id[tx],id[x]);
		x=fa[tx];tx=top[x];
	}
	if(dep[x]<dep[y])
		x^=y^=x^=y;
	modify(1,1,n,id[y],id[x]);
}
int query(int node,int l,int r,int ql,int qr){
	if(ql<=l && r<=qr)	return (sum[node]%mod);
	push_down(node,l,r);
	int mid=(l+r)>>1,anss=0;
	if(ql<=mid)	anss+=query(node<<1,l,mid,ql,qr);
	if(mid<qr) anss+=query(node<<1|1,mid+1,r,ql,qr);
	return anss%mod;
}
int line_query(int x,int y){
	int tx=top[x],ty=top[y],anss=0;
	while(tx!=ty){
		if(dep[tx]<dep[ty]){
			x^=y^=x^=y;tx^=ty^=tx^=ty;
		}
		anss+=query(1,1,n,id[tx],id[x]);
		anss%=mod;
		x=fa[tx];tx=top[x];
	}
	if(dep[x]<dep[y])
		x^=y^=x^=y;
	anss+=query(1,1,n,id[y],id[x]);
	return anss%mod;
}
signed main(){
	scanf("%lld %lld",&n,&q);
	for(int i=2,x;i<=n;i++){
		scanf("%lld",&x);add(++x,i);
	}
		
	dep[1]=1;
	dfs1(1,0);dfs2(1,1);
	for(int i=1,l,r,z;i<=q;i++){
		 scanf("%lld %lld %lld",&l,&r,&z);r++;z++;
		 ques[++cnt_q]=((node){i,l,z,0});ques[++cnt_q]=((node){i,r,z,1});
	}
	sort(ques+1,ques+1+cnt_q);now=0;
	for(int i=1;i<=cnt_q;i++){
		while(now<ques[i].pos)
			line_modify(1,++now);
		int num=ques[i].id;
		if(ques[i].flag==1)	ans[num].ans1=line_query(1,ques[i].z);
		else ans[num].ans2 =line_query(1,ques[i].z);
	}
	for(int i=1;i<=q;i++)
		printf("%lld\n",(ans[i].ans1-ans[i].ans2+mod)%mod);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值