洛谷 P4211 [LNOI2014]LCA(树剖+线段树,差分)

传送门


解题思路

调了一晚上。。紫题果然不是我现在能做的。。
首先考虑如何把多个deep的和转化成可以快速求出来的东西:
我们可以对于每个[l,r],把每个点到根节点的路径上的点权++(初始为0),这样对于每个询问(l,r,z),答案即为z到根节点的路径上的点权和。-----操作1
但是对于每个询问都操作一遍很显然会tle,所以再利用差分思想,把(l,r,z)转化为(1,r,z)-(1,l-1,z),具体来说,就是每次操作完后不清空点权,对询问进行排序,然后根据点编号从小到大依次进行操作1,并保存答案。
这样最多只会进行n次操作1。
具体如何实现操作1?
对于像这样路径上的区间加、区间求和操作,可以方便的使用树剖+线段树维护。

AC代码

#include<cstdio>
#include<iostream>
#include<cstring>
#include<iomanip>
#include<cmath>
#include<algorithm>
using namespace std;
const int maxn=(5e4+5)*4;
const int mod=201314;
struct node{
	int v,next;
}e[maxn];
struct Node{
	int r,z,v,id; 
}q[maxn];
int n,m,lazy[maxn],p[maxn],ans[maxn],cnt,num,deep[maxn],siz[maxn],son[maxn];
int tp[maxn],dfn[maxn],f[maxn],rk[maxn],sum[maxn];
bool cmp(Node a,Node b){
	return a.r<b.r;
}
void insert(int u,int v){
	cnt++;
	e[cnt].v=v;
	e[cnt].next=p[u];
	p[u]=cnt;
}
void dfs1(int u,int dep){
	deep[u]=dep;
	siz[u]=1;
	for(int i=p[u];i!=-1;i=e[i].next){
		int v=e[i].v;
		dfs1(v,dep+1);
		siz[u]+=siz[v];
		if(siz[v]>siz[son[u]]) son[u]=v;
	}
}
void dfs2(int u,int top){
	tp[u]=top;
	dfn[u]=++num;
	rk[num]=u;
	if(!son[u]) return;
	dfs2(son[u],top);
	for(int i=p[u];i!=-1;i=e[i].next){
		if(e[i].v==son[u]) continue;
		dfs2(e[i].v,e[i].v);
	}
}
inline void pushdown(int id,int l,int r){
	if(lazy[id]){
		int mid=(l+r)/2;
		sum[id*2]+=lazy[id]*(mid-l+1);
		sum[id*2+1]+=lazy[id]*(r-mid);
		lazy[id*2]+=lazy[id];
		lazy[id*2+1]+=lazy[id];
		lazy[id]=0;
	}
}
inline void pushup(int id){
	sum[id]=sum[id*2]+sum[id*2+1];
}
void update(int id,int l,int r,int x,int y){
	if(x<=l&&r<=y){
		lazy[id]++;
		sum[id]+=(r-l+1);
		return;
	}
	int mid=(l+r)/2;
	pushdown(id,l,r);
	if(x<=mid) update(id*2,l,mid,x,y);
	if(y>mid) update(id*2+1,mid+1,r,x,y);
	pushup(id);
}
int query(int id,int l,int r,int x,int y){
	if(x<=l&&r<=y){
		return sum[id]%mod;
	}
	int mid=(l+r)/2,res=0;
	pushdown(id,l,r);
	if(x<=mid) res+=query(id*2,l,mid,x,y);
	if(y>mid) res+=query(id*2+1,mid+1,r,x,y);
	return res%mod;
}
void add(int x){
	while(tp[x]!=1){
		update(1,1,n,dfn[tp[x]],dfn[x]);
		x=f[tp[x]];
	}
	update(1,1,n,1,dfn[x]);
}
int getans(int x){
	int res=0;
	while(tp[x]!=1){
		res=(res+query(1,1,n,dfn[tp[x]],dfn[x]))%mod;
		x=f[tp[x]];
	}
	res+=query(1,1,n,1,dfn[x]);
	return res%mod;
}
int main(){
	ios::sync_with_stdio(false);
	memset(p,-1,sizeof(p));
	cin>>n>>m;
	f[1]=1;
	for(int i=2;i<=n;i++){
		cin>>f[i];
		f[i]++;
		insert(f[i],i);
	}
	dfs1(1,1);
	dfs2(1,1);
	for(int i=1;i<=m;i++){
		int l,r,z;
		cin>>l>>r>>z;
		l++;
		r++;
		z++;
		q[i].r=l-1;
		q[i].z=z;
		q[i].v=-1;
		q[i].id=i;
		q[i+m].r=r;
		q[i+m].z=z;
		q[i+m].v=1;
		q[i+m].id=i;
	}
	sort(q+1,q+2*m+1,cmp);
	int now=1;
	for(int i=1;i<=2*m;i++){
		while(q[i].r==0) i++;
		while(now<=n&&now<=q[i].r){
			add(now);
			now++;
		}
		ans[q[i].id]+=q[i].v*getans(q[i].z);
	}
	for(int i=1;i<=m;i++) cout<<(ans[i]%mod+mod)%mod<<endl;
    return 0;
}
posted @ 2021-09-17 08:04  尹昱钦  阅读(23)  评论(0编辑  收藏  举报