BZOJ3626: [LNOI2014]LCA LCT

给出一个n个节点的有根树(编号为0到n-1,根节点为0)。一个点的深度定义为这个节点到根的距离+1。
设dep[i]表示点i的深度,LCA(i,j)表示i与j的最近公共祖先。
有q次询问,每次询问给出l r z,求sigma_{l<=i<=r}dep[LCA(i,z)]。
(即,求在[l,r]区间内的每个节点i与z的最近公共祖先的深度之和)
n,q<=50000
对于a和b,如果我们要求dep[LCA(a,b)],我们可以换一个求法:将a到根的路径上所有点权值+1,则b到根路径上所有点权值和就是dep[LCA(a,b)]。有了这个求法后我们就可以将询问离线,将一个询问拆成[0,r]对于z的贡献-[0,l-1]对于z的贡献,分别挂在r和l-1上,从0到n-1依次将每个节点到根路径+1并查询出挂在这个点上的询问即可。
答案要取模,注意负数要加回来。

#include<cstdio>
#define gm 50001
using namespace std;
typedef unsigned long long ll;
struct node
{
    node *s[2],*f;
    size_t add,sz,v;
    ll sum;
    node();
    inline char r(){return this==f->s[0]?0:this==f->s[1]?1:-1;}
    inline void up(){sz=s[0]->sz+1+s[1]->sz,sum=s[0]->sum+v+s[1]->sum;}
    inline void plus(size_t);
    inline void down()
    {
        if(~r()) f->down();
        if(add)
        {
            s[0]->plus(add);
            s[1]->plus(add);
            add=0;
        }
    }
}tr[gm],*nil=tr+50000;
node::node():f(tr+50000),add(),sz(1),sum(){s[0]=s[1]=tr+50000;}
inline void node::plus(size_t x=1)
{
    if(this!=nil)
    sum+=ll(sz)*x,add+=x,v+=x;
}
inline void rot(node *x)
{
    static node *o,*y;
    static char k;
    k=x->r();if(k==-1) return;
    k=!k;
    o=x->f,y=x->s[k];
    x->s[k]=o,o->s[!k]=y;
    if(~(k=o->r())) o->f->s[k]=x;
    x->f=o->f,o->f=x,y->f=o;
    o->up();
}
inline void splay(node *x)
{
    x->down();
    while(~x->r()) rot(x->f->r()==x->r()?x->f:x),rot(x);
    x->up();
}
inline void access(node *x)
{
    static node *y,*z;
    y=nil;z=x;
    while(x!=nil)
    {
        splay(x);
        x->s[1]=y;
        x->up();
        y=x;
        x=x->f;
    }
    splay(z);
}
inline void plus(node *x)
{
    access(x);
    x->plus();
}
inline int count(node *x)
{
    access(x);
    return x->sum%201314;
}
int n,q;
int ans[gm];
struct query
{
    bool plus;
    int no,z;
    query *n;
    query(int no,int z,bool plus,query *n):
        no(no),z(z),plus(plus),n(n){}
}*f[gm];
int main()
{
    scanf("%d%d",&n,&q);
    nil->sz=0;
    for(int i=1,fa;i<n;++i)
    {
        scanf("%d",&fa);
        tr[i].f=tr+fa;
    }
    for(int i=1,l,r,z;i<=q;++i)
    {
        scanf("%d%d%d",&l,&r,&z);
        if(l) f[l-1]=new query(i,z,0,f[l-1]);
        f[r]=new query(i,z,1,f[r]);
    }
    for(int i=0;i<n;++i)
    {
        plus(tr+i);
        for(query *j=f[i];j;j=j->n)
        {
            int res=count(tr+j->z);
            if(j->plus) ans[j->no]+=res;
            else ans[j->no]-=res;
        }
    }
    for(int i=1;i<=q;++i)
    {
        if(ans[i]<0) ans[i]+=201314;
        printf("%d\n",ans[i]);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值