BZOJ3626: [LNOI2014]LCA

BZOJ3626: [LNOI2014]LCA

Description

给出一个n个节点的有根树(编号为0到n-1,根节点为0)。

一个点的深度定义为这个节点到根的距离+1。

设dep[i]表示点i的深度,LCA(i,j)表示i与j的最近公共祖先。

有q次询问,每次询问给出l r 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。

题解Here!

又是树上的询问,首选 树链剖分/LCT。

这题就是树链剖分+线段树+差分。

deep[i]是什么?——就是从 i 点到根有多少个点(包括 i )。

我们从整体上考虑,发现对于一个询问:l , r , z 来说,所有的 lca 都在 z 到根的路径上。

从而有一些点,它们对很多的 lca 的深度都有贡献,而这个贡献等于在这个点下面的 lca 的个数,所以我们可以把每个 lca 到根的路径上的每个点的权值都加一。

然后从 z 向上走到根,沿路统计的权值就是答案了。

就是:对于一个询问: l , r , z ,我们把每个点 i ( l <= i <= r ) 到根的路径上的每一个点的权值都加一,因为:

所有的 lca 都在 z 到根的路径上,所以我们可以从 z 点向上爬到根,沿途统计的点权值之和,就是答案了。

我们每次的操作都是从某个点到根的,所以树链剖分+线段树就好了。

但是我们每次清空线段树,然后从 l ~ r 再添加一遍,树剖+线段树的复杂度就是 n*(logn)^2 的,还要做 q 次,复杂度依然不理想。

于是想到:差分可以将询问拆开,而且每个拆开的区间之间是有重叠的,是可以转移的,而不用每次都清空。

而且差分后的数组只与右端点有关!

所以我们可以将差分后的区间按照右端点从小到大排序(左端点都是根),然后按从小到大的顺序添加点,每遇到一个询问就查询一次。

于是一波sao操作就A了。。。

附代码:

#include<iostream>
#include<algorithm>
#include<cstdio>
#define LSON rt<<1
#define RSON rt<<1|1
#define DATA(x) b[x].data
#define SIGN(x) b[x].c
#define LSIDE(x) b[x].l
#define RSIDE(x) b[x].r
#define WIDTH(x) (RSIDE(x)-LSIDE(x)+1)
#define MAXN 50010
#define MOD 201314 
using namespace std;
int n,m,c=1,d=1,e=1;
int head[MAXN],deep[MAXN],fa[MAXN],son[MAXN],size[MAXN],id[MAXN],top[MAXN];
struct node1{
	int next,to;
}a[MAXN];
struct node2{
    int data,c,l,r;
}b[MAXN<<2];
struct node3{
    int x,u,id;
    bool flag;
}que[MAXN<<1];
struct node4{
    int l,r;
}ans[MAXN];
inline int read(){
	int date=0,w=1;char c=0;
	while(c<'0'||c>'9'){if(c=='-')w=-1;c=getchar();}
	while(c>='0'&&c<='9'){date=date*10+c-'0';c=getchar();}
	return date*w;
}
bool cmp(const node3 &x,const node3 &y){
    if(x.x==y.x)return x.id<y.id;
    return x.x<y.x;
}
inline void add(int x,int y){
	a[c].to=y;
	a[c].next=head[x];
	head[x]=c++;
}
inline void addque(int l,int r,int u,int i){
    que[e].x=l;que[e].u=u;que[e].id=i;
    que[e++].flag=false;
    que[e].x=r;que[e].u=u;que[e].id=i;
    que[e++].flag=true;
}
void dfs1(int rt){
	son[rt]=0;size[rt]=1;
	for(int i=head[rt];i;i=a[i].next){
		int will=a[i].to;
		if(!deep[will]){
			deep[will]=deep[rt]+1;
			fa[will]=rt;
			dfs1(will);
			size[rt]+=size[will];
			if(size[son[rt]]<size[will])son[rt]=will;
		}
	}
}
void dfs2(int rt,int f){
	id[rt]=d++;top[rt]=f;
	if(son[rt])dfs2(son[rt],f);
	for(int i=head[rt];i;i=a[i].next){
		int will=a[i].to;
		if(will!=fa[rt]&&will!=son[rt])
		dfs2(will,will);
	}
}
inline void pushup(int rt){
    DATA(rt)=(DATA(LSON)+DATA(RSON))%MOD;
}
inline void pushdown(int rt){
    if(!SIGN(rt)||LSIDE(rt)==RSIDE(rt))return;
    SIGN(LSON)=(SIGN(LSON)+SIGN(rt))%MOD;
    DATA(LSON)=(DATA(LSON)+SIGN(rt)*WIDTH(LSON))%MOD;
    SIGN(RSON)=(SIGN(RSON)+SIGN(rt))%MOD;
    DATA(RSON)=(DATA(RSON)+SIGN(rt)*WIDTH(RSON))%MOD;
    SIGN(rt)=0;
}
void buildtree(int l,int r,int rt){
    int mid;
    LSIDE(rt)=l;
    RSIDE(rt)=r;
    if(l==r){
        DATA(rt)=0;
        return;
    }
    mid=l+r>>1;
    buildtree(l,mid,LSON);
    buildtree(mid+1,r,RSON);
    pushup(rt);
}
void update(int l,int r,int c,int rt){
    int mid;
    if(l<=LSIDE(rt)&&RSIDE(rt)<=r){
        SIGN(rt)=(SIGN(rt)+c)%MOD;
        DATA(rt)=(DATA(rt)+c*WIDTH(rt))%MOD;
        return;
    }
    pushdown(rt);
    mid=LSIDE(rt)+RSIDE(rt)>>1;
    if(l<=mid)update(l,r,c,LSON);
    if(mid<r)update(l,r,c,RSON);
    pushup(rt);
}
int query(int l,int r,int rt){
    int mid,ans=0;
    if(l<=LSIDE(rt)&&RSIDE(rt)<=r)return DATA(rt);
    pushdown(rt);
    mid=LSIDE(rt)+RSIDE(rt)>>1;
    if(l<=mid)ans=(ans+query(l,r,LSON)%MOD)%MOD;
    if(mid<r)ans=(ans+query(l,r,RSON)%MOD)%MOD;
    return ans%MOD;
}
void work_update(int x,int y,int k){
    while(top[x]!=top[y]){
        if(deep[top[x]]<deep[top[y]])swap(x,y);
        update(id[top[x]],id[x],1,1);
        x=fa[top[x]];
    }
    if(deep[x]>deep[y])swap(x,y);
    update(id[x],id[y],1,1);
    return;
}
int work_query(int x,int y){
    int s=0;
    while(top[x]!=top[y]){
        if(deep[top[x]]<deep[top[y]])swap(x,y);
        s=(s+query(id[top[x]],id[x],1)%MOD)%MOD;
        x=fa[top[x]];
    }
    if(deep[x]>deep[y])swap(x,y);
    s=(s+query(id[x],id[y],1)%MOD)%MOD;
    return s;
}
void work(){
    int now=1,id;
    for(int i=1;i<e;i++){
        while(now<=que[i].x){
            work_update(1,now,1);
            now++;
        }
        id=que[i].id;
        if(que[i].flag)ans[id].r=work_query(1,que[i].u);
        else ans[id].l=work_query(1,que[i].u);
    }
    for(int i=1;i<=m;i++)printf("%d\n",(ans[i].r-ans[i].l+MOD)%MOD);
}
void init(){
	int x,l,r,u;
	n=read();m=read();
	for(int i=2;i<=n;i++){
		x=read()+1;
		add(x,i);
	}
	deep[1]=1;
	dfs1(1);
	dfs2(1,1);
	buildtree(1,n,1);
	for(int i=1;i<=m;i++){
		l=read();r=read()+1;u=read()+1;
		addque(l,r,u,i);
	}
	sort(que+1,que+e,cmp);
}
int main(){
	init();
	work();
	return 0;
}

 

posted @ 2018-05-13 15:27  符拉迪沃斯托克  阅读(199)  评论(0编辑  收藏  举报
Live2D