BZOJ 3786 星系探索 splay维护dfs序

2 篇文章 0 订阅

一开始看到子树操作想要树剖,但是仔细想想之后发现树剖没有办法维护一个点到根节点的距离,而且无法维护对应关系,后来就想到了使用dfs序,这样只要维护前缀和就可以快速求出一个点到根节点的距离,由于有换边操作,所以我们要使用splay维护,这道题要注意一个细节,就是每次想要提取出来一个区间需要先找到这个区间的前驱后继,都做完以后这题就能过啦

#include<cstdio>
#include<cstdlib>
#include<ctime>
#include<cmath>
#include<cstring>
#include<string>
#include<iostream>
#include<iomanip>
#include<algorithm>
using namespace std;
typedef long long ll;
struct splay
{
    splay *fa,*ls,*rs;
    ll v,sum,zsize,fsize,add_mark;
    bool pr;
    splay();
    void push_up();
    void add_v(ll val);
    void push_down();
}*null=new splay(),mempool[300000],*root;
int dfsx[300000];
int in_t[300000];
int out_t[300000];
int fir[300000];
int nex[300000];
struct bian
{
    int r;
}a[600000];
int topppp;
ll v[300000];
int top=0;
void dfs(int u,int fro)
{
    dfsx[++top]=u;
    in_t[u]=top;
    for(int o=fir[u];o!=0;o=nex[o])
        if(a[o].r!=fro)
            dfs(a[o].r,u);
    dfsx[++top]=u;
    out_t[u]=top;
}
void splay :: push_up()
{
    sum=ls->sum+v+rs->sum;
    zsize=ls->zsize+pr+rs->zsize;
    fsize=ls->fsize+(1-pr)+rs->fsize;
}
void splay :: push_down()
{
    if(add_mark)
    {
        if(ls!=null) ls->add_v(add_mark);
        if(rs!=null) rs->add_v(add_mark);
        add_mark=0;
    }
}
void splay :: add_v(ll val)
{
    if(pr) v+=val;
    else v-=val;
    sum+=(zsize-fsize)*val;
    add_mark+=val;
}
splay :: splay()
{
    fa=ls=rs=null;
    v=sum=zsize=fsize=add_mark=pr=0;
}
void right(splay *x)
{
    splay *y=x->fa;
    y->ls=x->rs;
    x->rs->fa=y;
    x->rs=y;
    x->fa=y->fa;
    if(y==y->fa->ls) y->fa->ls=x;
    if(y==y->fa->rs) y->fa->rs=x;
    y->fa=x;
    y->push_up();
    if(y==root) root=x;
}
void left(splay *x)
{
    splay *y=x->fa;
    y->rs=x->ls;
    x->ls->fa=y;
    x->ls=y;
    x->fa=y->fa;
    if(y==y->fa->ls) y->fa->ls=x;
    if(y==y->fa->rs) y->fa->rs=x;
    y->fa=x;
    y->push_up();
    if(y==root) root=x;
}
void push_down(splay *x)
{
    if(x->fa!=null) push_down(x->fa);
    x->push_down();
}
void splaying(splay *x,splay *goal)
{
    push_down(x);
    while(1)
    {
        splay *y=x->fa;
        splay *z=y->fa;
        //cout<<x<<" "<<y->rs<<endl;//<<null<<" "<<endl;
        if(y==goal) break;
        if(z==goal)
        {
            if(x==y->ls) right(x);
            else left(x);
            break;
        }
        if(x==y->ls)
        {
            if(y==z->ls) right(y);
            right(x);
        }
        else if(x==y->rs)
        {
            if(y==z->rs) left(y);
            left(x);
        }
    }
    x->push_up();
}
splay *maketree(int l,int r)
{
    if(l>r) return null;
    int mid=l+r>>1;
    splay *x=&mempool[mid];
    if(mid==in_t[dfsx[mid]])
    {
        x->v=v[dfsx[mid]];
        x->sum=v[dfsx[mid]];
        x->pr=true;
        x->zsize=1;
    }
    else
    {
        x->v=-v[dfsx[mid]];
        x->sum=-v[dfsx[mid]];
        x->pr=false;
        x->fsize=1;
    }
    x->ls=maketree(l,mid-1);
    x->ls->fa=x;
    x->rs=maketree(mid+1,r);
    x->rs->fa=x;
    x->push_up();
    return x;
}
splay* find_hou(splay *o)
{
    if(o->ls!=null) return find_hou(o->ls);
    return o;
}
splay* find_qian(splay *o)
{
    if(o->rs!=null) return find_qian(o->rs);
    return o;
}
ll find_ans(int x)
{
    splaying(&mempool[in_t[1]],null);
    splay *mid=find_qian(root->ls);
    splaying(&mempool[in_t[x]],null);
    splay *midd=find_hou(root->rs);
    splaying(mid,null);
    splaying(midd,root);
    return root->rs->ls->sum;
}
void change(int x,int v)
{
    splaying(&mempool[in_t[x]],null);
    splay *mid=find_qian(root->ls);
    splaying(&mempool[out_t[x]],null);
    splay *midd=find_hou(root->rs);
    splaying(mid,null);
    splaying(midd,root);
    root->rs->ls->add_v(v);
    root->rs->push_up();
    root->push_up();
}
void move_to_wz(int x,int y)
{
    splaying(&mempool[in_t[x]],null);
    splay *mid=find_qian(root->ls);
    splaying(&mempool[out_t[x]],null);
    splay *midd=find_hou(root->rs);
    splaying(mid,null);
    splaying(midd,root);
    splay *qie=root->rs->ls;
    qie->fa=null;
    root->rs->ls=null;
    root->rs->push_up();
    root->push_up();
    splaying(&mempool[in_t[y]],null);
    splay *middd=find_hou(root->rs);
    splaying(middd,root);
    root->rs->ls=qie;
    qie->fa=root->rs;
    root->rs->push_up();
    root->push_up();
}
void add_edge(int l,int r)
{
    //cout<<l<<" "<<r<<endl;
    a[++topppp].r=r;
    nex[topppp]=fir[l];
    fir[l]=topppp;
}
char s[10];
int main()
{
    ll n,m;
    scanf("%lld",&n);
    for(int i=1;i<n;i++)
    {
        int x;
        scanf("%d",&x);
        add_edge(x,i+1);
    }
    for(int i=1;i<=n;i++) scanf("%lld",&v[i]);
    dfs(1,0);
    root=maketree(0,top+1);
    scanf("%lld",&m);
    for(int i=1;i<=m;i++)
    {

        scanf("%s",s);
        if(s[0]=='Q')
        {
            int x;
            scanf("%d",&x);
            printf("%lld\n",find_ans(x));

        }
        else if(s[0]=='F')
        {
            ll x,val;
            scanf("%lld%lld",&x,&val);
            change(x,val);
        }
        else
        {
            int x,y;
            scanf("%d%d",&x,&y);
            move_to_wz(x,y);
        }
    }

}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值