[LNOI2014]LCA

Description

\(q\) 次询问,每次给定 \(l,r,z\) ,求

\[\sum_{i=l}^r dep[Lca(i,z)] \]

Solution

如果不转换一下的话,应该是相当不好求。可以这样想,求两点 u 和 v 的 lca 的深度,其实就是求 u 和 v 到根的路径的交的大小。那么可以对其中一个点,修改其到根节点路径上的值,都加一。然后再查询另一个点到根节点路径上权值和。可以考虑树剖。

类比这个思路,我们可以修改 l 到 r 的值,然后询问 z;也可以修改 z 然后询问 l 到 r。但是后者显然不能进一步优化。而前者的话,我们可以把它拆成两个前缀相减。那么后面的思路就很容易了,可以把所有 l 和 r 的询问离线,然后碰到就修改。

#include<stdio.h>
#include<algorithm>
using namespace std;
#define lid id<<1
#define rid id<<1|1

const int Mod=201314;
const int N=1e5+7;

inline int read(){
    int x=0,flag=1; char c=getchar();
    while(c<'0'||c>'9'){if(c=='-') flag=0;c=getchar();}
    while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+c-48;c=getchar();}
    return flag? x:-x;
}

struct Que{
    int l,pos,op,u;
    bool operator <(const Que &X) const{return l<X.l;}
}q[N];

struct E{
    int next,to;
}e[N];

struct Node{
    int s,tag;
}t[N<<2];

int head[N],n,m,ans[N];;
int sz[N],top[N],son[N],dep[N],fa[N],in[N];

inline void add(int id,int to){
    static int cnt=0;
    e[++cnt]=(E){head[id],to};
    head[id]=cnt;
}

void dfs(int u){
    sz[u]=1;
    for(int i=head[u];i;i=e[i].next){
        int v=e[i].to;
        fa[v]=u,dep[v]=dep[u]+1;
        dfs(v),sz[u]+=sz[v];
        if(sz[u]>sz[son[u]]) son[u]=v;
    }
}

void Dfs(int u,int tp){
    static int tot=0;
    top[u]=tp,in[u]=++tot;
    if(!son[u]) return ;
    Dfs(son[u],tp);
    for(int i=head[u];i;i=e[i].next){
        int v=e[i].to;
        if(v!=son[u]) Dfs(v,v);
    }
}

inline void update(int id){t[id].s=t[lid].s+t[rid].s;}

inline void push(int id,int val,int v){
    t[id].s=(t[id].s+val*v%Mod)%Mod;
    t[id].tag=(t[id].tag+val)%Mod;
}

void pushdown(int id,int lf,int rf){
    int mid=(lf+rf)>>1;
    push(lid,t[id].tag,mid-lf+1);
    push(rid,t[id].tag,rf-mid);
    t[id].tag=0;
}

int L,R;
void modify(int id=1,int lf=1,int rf=n){
    if(L<=lf&&rf<=R){push(id,1,rf-lf+1);return ;}
    int mid=(lf+rf)>>1;
    if(t[id].tag) pushdown(id,lf,rf);
    if(L<=mid) modify(lid,lf,mid);
    if(R>mid) modify(rid,mid+1,rf);
    update(id);
}

int query(int id=1,int lf=1,int rf=n){
    if(L<=lf&&rf<=R) return t[id].s;
    int mid=(lf+rf)>>1,ret=0;
    if(t[id].tag) pushdown(id,lf,rf);
    if(L<=mid) ret=(ret+query(lid,lf,mid))%Mod;
    if(R>mid) ret=(ret+query(rid,mid+1,rf))%Mod;
    return ret;
}

void Modify(int u){
    while(top[u]!=top[1]){
        L=in[top[u]],R=in[u];
        modify(),u=fa[top[u]];
    }
    L=1,R=in[u],modify();
}

int Query(int u){
    int ret=0;
    while(top[u]!=top[1]){
        L=in[top[u]],R=in[u];
        ret=(ret+query())%Mod;
        u=fa[top[u]];
    }
    L=1,R=in[u];
    return (ret+query())%Mod;
}

int main(){
    n=read(),m=read();
    for(int i=2;i<=n;i++) add(read()+1,i);
    for(int i=1;i<=m;i++){
        int l=read()+1,r=read()+1,u=read()+1;
        q[(i<<1)-1]=(Que){r,i,1,u};
        q[i<<1]=(Que){l-1,i,-1,u};
    }
    sort(q+1,q+1+2*m);
    dep[1]=1,dfs(1),Dfs(1,1);
    for(int i=1,now=0;i<=2*m;i++){
        while(now!=q[i].l) Modify(++now);
        ans[q[i].pos]+=q[i].op*Query(q[i].u);
        ans[q[i].pos]=(ans[q[i].pos]%Mod+Mod)%Mod;
    }
    for(int i=1;i<=m;i++)
        printf("%d\n",ans[i]);
}
posted @ 2021-04-15 17:30  Kreap  阅读(39)  评论(0编辑  收藏  举报