洛谷 P4211 [LNOI2014]LCA 树链剖分

题目描述

给出一个n个节点的有根树(编号为0到n-1,根节点为0)。一个点的深度定义为这个节点到根的距离+1。 设dep[i]表示点i的深度,LCA(i,j)表示i与j的最近公共祖先。 有q次询问,每次询问给出l r z,求 l i r d e p [ L C A ( i , z ) ]

输入输出格式

输入格式:
第一行2个整数n q。 接下来n-1行,分别表示点1到点n-1的父节点编号。 接下来q行,每行3个整数l r z。

输出格式:
输出q行,每行表示一个询问的答案。每个答案对201314取模输出

输入输出样例

输入样例#1:
5 2
0
0
1
1
1 4 3
1 4 2
输出样例#1:
8
5
说明

共5组数据,n与q的规模分别为10000,20000,30000,40000,50000。

分析:
之前做过一道差不多的题目,是差分做的。
我们可以把从根到 z 路径上的点权都加 1 ,然后从编号为 l r 的节点往根走,这些路径的权值和即为所求。

然后我们可以换一种想法,把 l r 上到根每一条路径点权都加1,求 z 到根路径权值和。
所以我们可以把询问离线,并拆成询问 z [ 1 , r ] 这些边上的权值和减去 [ 1 , l 1 ] 上的权值和。
然后我们一次插入编号为 i 的点的路径,然后解决 [ 1 , i ] 的询问,然后最后合并答案即可。

路径修改+路径查询可以用树链剖分解决,时间复杂度为 O ( n l o g 2 n ) ,记得取模。

代码:

#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
#define LL long long 

const LL mod=201314;
const LL maxn=100007;

using namespace std;

LL n,test,x,y,z,cnt;
LL ls[maxn];
LL dfn[maxn],top[maxn],size[maxn],dep[maxn],fa[maxn];

struct edge{
    LL y,next;
}g[maxn*2];

struct rec{
    LL x,l,num;
    LL ans;
}q[maxn*2];

struct node{
    LL sum,lazy;
}t[maxn*2];

bool cmp1(rec x,rec y)
{
    return x.l<y.l;
}

bool cmp2(rec x,rec y)
{
    return x.num<y.num;
}

void add(LL x,LL y)
{
    g[++cnt]=(edge){y,ls[x]};
    ls[x]=cnt;
}

void dfs1(LL x,LL f)
{
    size[x]=1;
    dep[x]=dep[f]+1;
    fa[x]=f;
    for (LL i=ls[x];i>0;i=g[i].next)
    {
        LL y=g[i].y;
        if (y!=fa[x])
        {
            dfs1(y,x);
            size[x]+=size[y];
        }
    }
}

void dfs2(LL x,LL f)
{
    top[x]=f;
    dfn[x]=++cnt;
    LL c=maxn-1;
    for (LL i=ls[x];i>0;i=g[i].next)
    {
        LL y=g[i].y;
        if (y==fa[x]) continue;
        if (size[y]>size[c]) c=y;
    }
    if (c==maxn-1) return;
    dfs2(c,f);
    for (LL i=ls[x];i>0;i=g[i].next)
    {
        LL y=g[i].y;
        if ((y==fa[x]) || (y==c)) continue;
        dfs2(y,y);
    }
}

void ins(LL p,LL l,LL r,LL x,LL y,LL k)
{
    if ((l==x) && (r==y))
    {
        t[p].lazy=(t[p].lazy+k)%mod;
        t[p].sum=(t[p].sum+(r-l+1)*k)%mod;
        return;
    }
    LL mid=(l+r)/2;
    if (t[p].lazy)
    {
        t[p*2].lazy=(t[p*2].lazy+t[p].lazy)%mod;
        t[p*2+1].lazy=(t[p*2+1].lazy+t[p].lazy)%mod;
        t[p*2].sum=(t[p*2].sum+(mid-l+1)*1ll*t[p].lazy)%mod;
        t[p*2+1].sum=(t[p*2+1].sum+(r-mid)*1ll*t[p].lazy)%mod;
        t[p].lazy=0;
    }
    if (y<=mid) ins(p*2,l,mid,x,y,k);
    else if (x>mid) ins(p*2+1,mid+1,r,x,y,k);
        else
        {
            ins(p*2,l,mid,x,mid,k);
            ins(p*2+1,mid+1,r,mid+1,y,k);
        }
    t[p].sum=(t[p*2].sum+t[p*2+1].sum)%mod; 
}

LL getsum(LL p,LL l,LL r,LL x,LL y)
{
    if ((l==x) && (r==y)) return t[p].sum;
    LL mid=(l+r)/2;
    if (t[p].lazy)
    {
        t[p*2].lazy=(t[p*2].lazy+t[p].lazy)%mod;
        t[p*2+1].lazy=(t[p*2+1].lazy+t[p].lazy)%mod;
        t[p*2].sum=(t[p*2].sum+(mid-l+1)*1ll*t[p].lazy)%mod;
        t[p*2+1].sum=(t[p*2+1].sum+(r-mid)*1ll*t[p].lazy)%mod;
        t[p].lazy=0;
    }
    if (y<=mid) return getsum(p*2,l,mid,x,y);
    else if (x>mid) return getsum(p*2+1,mid+1,r,x,y);
        else return (getsum(p*2,l,mid,x,mid)+getsum(p*2+1,mid+1,r,mid+1,y))%mod;
}

LL ask(LL x,LL y)
{
    LL ans=0;
    while (top[x]!=top[y])
    {
        if (dep[top[x]]>dep[top[y]]) swap(x,y);
        ans=(ans+getsum(1,1,n,dfn[top[y]],dfn[y]))%mod;
        y=fa[top[y]];
    }
    if (dep[x]>dep[y]) swap(x,y);
    ans=(ans+getsum(1,1,n,dfn[x],dfn[y]))%mod;
    return ans;
}

void change(LL x,LL y)
{
    while (top[x]!=top[y])
    {
        if (dep[top[x]]>dep[top[y]]) swap(x,y);
        ins(1,1,n,dfn[top[y]],dfn[y],1);
        y=fa[top[y]];
    }
    if (dep[x]>dep[y]) swap(x,y);
    ins(1,1,n,dfn[x],dfn[y],1);
}

int main()
{
    scanf("%lld%lld",&n,&test);
    for (LL i=1;i<n;i++)
    {
        scanf("%lld",&x);
        add(x,i);
        add(i,x);
    }
    for (LL i=1;i<=test;i++)
    {
        scanf("%lld%lld%lld",&x,&y,&z);
        q[i*2-1]=(rec){z,x-1,i*2-1};
        q[i*2]=(rec){z,y,i*2};
    }
    sort(q+1,q+2*test+1,cmp1);
    dfs1(0,0);
    cnt=0;
    dfs2(0,0);
    LL j=-1;
    for (LL i=1;i<=test*2;i++)
    {
        while (j<q[i].l)
        {
            j++;
            change(0,j);
        }
        if (q[i].l==-1) q[i].ans=0;
                   else q[i].ans=ask(0,q[i].x);
    }
    sort(q+1,q+2*test+1,cmp2);
    for (LL i=1;i<=test;i++)
    {
        printf("%lld\n",(q[i*2].ans+mod-q[i*2-1].ans)%mod);
    }
}

猜你喜欢

转载自blog.csdn.net/liangzihao1/article/details/81056724