bzoj3626 [LNOI2014]LCA——树链剖分

题目:https://www.lydsy.com/JudgeOnline/problem.php?id=3626

思路很巧妙,把区间换成前缀和相减;

把 l ~ r 到根路径上的点的点权都+1,然后 z 到根求和,就是 z 与 l  ~ r 每个点 lca 深度的和;

这里若要用前缀和,则需要把询问离线排序;

然后上树链剖分,修改和求和线段树解决即可。

代码如下:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
int const maxn=50005,mod=201314;
int n,m,fa[maxn],head[maxn],ct,dfn[maxn],tim;
int top[maxn],to[maxn],siz[maxn],tl[maxn],tr[maxn];
struct N{
    int to,next;
    N(int t=0,int n=0):to(t),next(n) {}
}edge[maxn<<1];
struct T{ll s,lzy,size;}t[maxn<<2];
struct Q{int l,r,z,bh;ll ansl,ansr;}q[maxn];
void add(int x,int y){edge[++ct]=N(y,head[x]);head[x]=ct;}
bool cmp(int x,int y){return q[x].l<q[y].l;}
bool cmp2(int x,int y){return q[x].r<q[y].r;}
int rd()
{
    int ret=0;char ch=getchar();
    while(ch<'0'||ch>'9')ch=getchar();
    while(ch>='0'&&ch<='9')ret=ret*10+ch-'0',ch=getchar();
    return ret;
}
void dfs(int x,int f)
{
    siz[x]=1;
    for(int i=head[x];i;i=edge[i].next)
    {
        int u=edge[i].to;
        if(u==f)continue;
        dfs(u,x); siz[x]+=siz[u];
        if(siz[u]>siz[to[x]])to[x]=u;
    }
}
void dfs2(int x)
{
    dfn[x]=++tim;
    if(to[x])top[to[x]]=top[x],dfs2(to[x]);
    for(int i=head[x];i;i=edge[i].next)
    {
        int u=edge[i].to;
        if(u==fa[x]||u==to[x])continue;
        top[u]=u; //top[u]!=x !!!
        dfs2(u);
    }
}
void pushup(int x)
{
    t[x].size=t[x<<1].size+t[x<<1|1].size;
    t[x].s=t[x<<1].s+t[x<<1|1].s;
}
void pushdown(int x)
{
    if(!t[x].lzy)return;
    int ls=(x<<1),rs=(x<<1|1);
    ll l=t[x].lzy;t[x].lzy=0;
    t[ls].s+=l*t[ls].size; t[ls].lzy+=l;
    t[rs].s+=l*t[rs].size; t[rs].lzy+=l;
}
void build(int x,int l,int r)
{
    if(l==r){t[x].size=1; return;}
    int mid=((l+r)>>1);
    build(x<<1,l,mid); build(x<<1|1,mid+1,r);
    pushup(x);
}
void update(int x,int l,int r,int L,int R)
{
    if(l>=L&&r<=R)
    {
        t[x].s+=t[x].size; t[x].lzy++;
        return;
    }
    pushdown(x);
    int mid=((l+r)>>1);
    if(mid>=L)update(x<<1,l,mid,L,R);
    if(mid<R)update(x<<1|1,mid+1,r,L,R);
    pushup(x);
}
void work(int x)//x 到 rt 路径上点权+1 
{
    while(x)
    {
        update(1,1,n,dfn[top[x]],dfn[x]);
        x=fa[top[x]];
    }
//    update(1,1,n,dfn[rt],dfn[rt]);
}
ll query(int x,int l,int r,int L,int R)
{
    if(l>=L&&r<=R)return t[x].s;
    int mid=((l+r)>>1); ll ret=0;
    pushdown(x);
    if(mid>=L)ret+=query(x<<1,l,mid,L,R);
    if(mid<R)ret+=query(x<<1|1,mid+1,r,L,R);
    return ret;
}
ll qry(int x)
{
    ll ret=0;
    while(x)
    {
        ret+=query(1,1,n,dfn[top[x]],dfn[x]);
        x=fa[top[x]];
    }
    return ret;
}
int main()
{
    n=rd();m=rd();
    for(int i=2;i<=n;i++){fa[i]=rd()+1; add(fa[i],i); add(i,fa[i]);}
    for(int i=1;i<=m;i++)
    {
        q[i].l=rd()+1; q[i].r=rd()+1; q[i].z=rd()+1;
        tl[i]=i; tr[i]=i;
    }
    sort(tl+1,tl+m+1,cmp);
    sort(tr+1,tr+m+1,cmp2);
    dfs(1,0); top[1]=1; dfs2(1); build(1,1,n);
    int dl=1,dr=1;
    while(q[tl[dl]].l==1)dl++;
    for(int i=1;i<=n;i++)
    {
        work(i);
        while(q[tl[dl]].l-1==i&&dl<=m)
        {
            q[tl[dl]].ansl=qry(q[tl[dl]].z);//不套 dfn !!! 
            dl++;
        }
        while(q[tr[dr]].r==i&&dr<=m)
        {
            q[tr[dr]].ansr=qry(q[tr[dr]].z);
            dr++;
        }
    }
    for(int i=1;i<=m;i++)
        printf("%lld\n",(q[i].ansr-q[i].ansl)%mod);
    return 0;
}

 

posted @ 2018-06-14 23:32  Zinn  阅读(179)  评论(0编辑  收藏  举报