BZOJ3626 [LNOI2014]LCA

16 篇文章 0 订阅
7 篇文章 0 订阅

链剖,由于询问是询问一段区间内的所以我们建主席树,造两颗主席树,一个维护点的个数,一个维护点的深度和,对于每个点x,往根走,每走一条轻边到达轻边的父亲结点f,就在主席树上给f的点个数+1,深度和+f的深度(默认刚开始是走了一条轻边到达点x的)

查询的时候每走一条轻边到达一条重链上的结点x,求出重链上x和x以下有多少个点,再减去从来那条轻边底下来的点的个数,剩下这些点和询问点的lca都是x,用点的个数*x的深度记入答案,对于重链上x以上的点,LCA就是那些点,求深度和加到答案里,然后走到重链顶端的轻边上边并更新从轻边带上来的点数(默认刚开始是走了一条轻边到达询问点)

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<iomanip>
#include<ctime>
#include<vector>
#include<stack>
#include<set>
#include<bitset>
#include<map>
#include<queue>
using namespace std;
#define MAXN 50010
#define MAXM 10000010
#define MOD 201314
#define INF 1000000000
#define eps 1e-8
#define ll long long 
struct vec{
    int to;
    int fro;
};
vec mp[MAXN*2];
int tai[MAXN],cnt;
int siz[MAXN],dep[MAXN],fa[MAXN],son[MAXN],dfn[MAXN],ndf[MAXN],tp[MAXN],lw[MAXN],tim;
int n,m,q;
int srt[MAXN],drt[MAXN];
int v[MAXM],Son[MAXM][2];
int tot;
int now;
inline void be(int x,int y){
    mp[++cnt].to=y;
    mp[cnt].fro=tai[x];
    tai[x]=cnt;
}
inline void bde(int x,int y){
    be(x,y);
    be(y,x);
}
void dfs1(int x){
    int i,y;
    siz[x]=1;
    dep[x]=dep[fa[x]]+1;
    for(i=tai[x];i;i=mp[i].fro){
        y=mp[i].to;
        if(!siz[y]){
            fa[y]=x;
            dfs1(y);
            siz[x]+=siz[y];
            if(siz[y]>siz[son[x]]){
                son[x]=y;
            }
        }
    }
}
void dfs2(int x,int z){
    int i,y;
    tp[x]=z;
    lw[z]=x;
    dfn[x]=++tim;
    ndf[tim]=x;
    if(son[x]){
        dfs2(son[x],z);
        for(i=tai[x];i;i=mp[i].fro){
            y=mp[i].to;
            if(!dfn[y]){
                dfs2(y,y);
            }
        }
    }
}
void change(int &x,int xx,int y,int z,int p,int cv){
    x=++tot;
    v[x]=(v[xx]+cv)%MOD;
    memcpy(Son[x],Son[xx],sizeof(Son[x]));
    if(y==z){
        return ;
    }
    int mid=y+z>>1;
    if(p<=mid){
        change(Son[x][0],Son[xx][0],y,mid,p,cv);
    }else{
        change(Son[x][1],Son[xx][1],mid+1,z,p,cv);
    }
}
void tochange(int x){
    while(x){
        change(srt[now],srt[now],1,n,dfn[x],1);
        change(drt[now],drt[now],1,n,dfn[x],dep[x]);
        x=fa[tp[x]];
    }
}
int ask(int x,int xx,int y,int z,int l,int r){
    if(l>r||!l){
        return 0;
    }
    if(y==l&&z==r){
        return (v[xx]-v[x]+MOD)%MOD;
    }
    int mid=y+z>>1;
    if(r<=mid){
        return ask(Son[x][0],Son[xx][0],y,mid,l,r);
    }else if(l>mid){
        return ask(Son[x][1],Son[xx][1],mid+1,z,l,r);
    }else{
        return (ask(Son[x][0],Son[xx][0],y,mid,l,mid)+ask(Son[x][1],Son[xx][1],mid+1,z,mid+1,r))%MOD;
    }
}
int toask(int l,int r,int x){
    int lu=0;
    int re=0;
    while(x){
    	(re+=(ll)ask(srt[l-1],srt[r],1,n,dfn[son[x]],dfn[lw[tp[x]]])*dep[x]%MOD)%=MOD;
    	(re+=(ll)(ask(srt[l-1],srt[r],1,n,dfn[x],dfn[x])-lu)*dep[x]%MOD)%=MOD;
        (re+=ask(drt[l-1],drt[r],1,n,dfn[tp[x]],dfn[fa[x]]))%=MOD;
        lu=ask(srt[l-1],srt[r],1,n,dfn[tp[x]],dfn[lw[tp[x]]]);
        x=fa[tp[x]];    
    }
    return re;
}
int main(){
    int i,x,y,l,r;	
    scanf("%d%d",&n,&m);
    for(i=2;i<=n;i++){
        scanf("%d",&x);
        x++;
        bde(x,i);
    }
    dfs1(1);
    dfs2(1,1);
    for(now=1;now<=n;now++){
    	srt[now]=srt[now-1];
    	drt[now]=drt[now-1];
    	tochange(now);
    }
    while(m--){
        scanf("%d%d%d",&l,&r,&x);
        l++;
        r++;
        x++;
        printf("%d\n",toask(l,r,x));
    }
    return 0;
}
 
/*
7 100
0 0 1 1 2 2 


*/


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值