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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章