【GDSOI2017】 中学生数据结构题

Description

给出一棵点权有根树,要求:
1. 树上的路径区间加
2. 树上路径区间查询和
3. 树上路径整体旋转一位(如:原路径上的权值依次是这样的:1,2,3,4,操作完后变成:4,1,2,3)
n<=100000

链剖+splay

这里不讲

LCT

LCT上的splay维护的是形态,而我们权值轮换的同时形态没有发生改变
于是我们要新开一棵splay专门维护权值,而且满足中序遍历与形态splay一致。
当shift时,只在权值splay上操作一番。其他操作一起维护即可。时刻注意维护好形态splay的根与权值splay的根的一一映射。

细节注意:

  1. parent边可以只对形态splay维护,有关点权的信息可以只对权值splay维护。其余的两个都要分别维护。
  2. 时刻注意维护好形态splay的根与权值splay的根的一一映射(非常重要)。

Code

#include<cstdio>
#include<cstring>
#include<algorithm>
#define fo(i,a,b) for(int i=a;i<=b;++i)
#define fd(i,a,b) for(int i=a;i>=b;--i)
#define max(x,y) ((x)>(y)?(x):(y))
#define min(x,y) ((x)<(y)?(x):(y))
#define mset(a,x) memset(a,x,sizeof(a))
using namespace std;
typedef long long ll;
const int N=2e5+5;
int n,sta[N];
int f[2][N],a[2][N][2],size[2][N],p[N],fx[2][N];
ll tag[N],sm[N],key[N];
bool rev[2][N];
bool pd(int o,int x){return x==a[o][f[o][x]][1];}
void update(int o,int x)
{
    size[o][x]=1+size[o][a[o][x][0]]+size[o][a[o][x][1]];
    if(o==1) sm[x]=key[x]+sm[a[o][x][0]]+sm[a[o][x][1]];
}
void turn(int o,int x)
{
    if(!x) return;
    swap(a[o][x][0],a[o][x][1]);
    rev[o][x]^=1;
}
void add(int x,ll z)
{
    if(!x) return;
    key[x]+=z,sm[x]+=size[1][x]*z,tag[x]+=z;
}
void down(int o,int x)
{
    if(rev[o][x])
    {
        turn(o,a[o][x][0]);turn(o,a[o][x][1]);
        rev[o][x]=0;
    }
    if(o==1 && tag[x])
    {
        add(a[o][x][0],tag[x]);add(a[o][x][1],tag[x]);
        tag[x]=0;
    }
}
void remove(int o,int x,int y)
{
    for(;x!=y;x=f[o][x]) sta[++sta[0]]=x;
    while(sta[0]) down(o,sta[sta[0]--]);
}
void rotate(int o,int x)
{
    int y=f[o][x],z=pd(o,x);
    f[o][x]=f[o][y];
    if(f[o][y]) a[o][f[o][y]][pd(o,y)]=x;
    else
    {
        if(o==0) p[x]=p[y],p[y]=0;
        fx[o][x]=fx[o][y],fx[o][y]=0;
        fx[1-o][fx[o][x]]=x;
    }
    a[o][y][z]=a[o][x][1-z];
    if(a[o][x][1-z]) f[o][a[o][x][1-z]]=y;
    f[o][y]=x,a[o][x][1-z]=y;
    update(o,y);
}
void splay(int o,int x,int y=0)
{
    remove(o,x,y);
    for(;f[o][x]!=y;rotate(o,x))
        if(f[o][f[o][x]]!=y) rotate(o,pd(o,x)==pd(o,f[o][x])?f[o][x]:x);
    update(o,x);
}
int kth(int o,int x,int k)
{
    if(size[o][a[o][x][0]]+1==k) return x;
    down(o,x);
    if(k<=size[o][a[o][x][0]]) return kth(o,a[o][x][0],k);
    else return kth(o,a[o][x][1],k-1-size[o][a[o][x][0]]);
}
int find(int x)
{
    if(!x) return 0;
    splay(0,x);
    int rk=size[0][a[0][x][0]]+1;
    return kth(1,fx[0][x],rk);
}
void access(int x)
{
    for(int y=0;x;y=x,x=p[x])
    {
        int u=find(x),v=find(y);
        splay(0,x);
        splay(1,u);
        f[0][a[0][x][1]]=0,p[a[0][x][1]]=x;
        f[1][a[1][u][1]]=0;
        fx[0][a[0][x][1]]=a[1][u][1];
        fx[1][a[1][u][1]]=a[0][x][1];
        a[0][x][1]=y,f[0][y]=x,p[y]=0;
        a[1][u][1]=v,f[1][v]=u;
        update(0,x);
        update(1,u);
    }
}
void makeroot(int x)
{
    access(x);
    int u=find(x);
    splay(0,x);splay(1,u);
    turn(0,x);turn(1,u);
}
void link(int x,int y){makeroot(x);p[x]=y;}
int main()
{
    freopen("shift.in","r",stdin);
    freopen("shift.out","w",stdout);
    int Q,x,y,z;
    scanf("%d",&n);
    fo(i,1,n) fx[0][i]=fx[1][i]=i;
    fo(i,1,n-1) scanf("%d %d",&x,&y),link(x,y);
    char ch;
    for(scanf("%d\n",&Q);Q;Q--)
    {
        scanf("%c",&ch);
        if(ch=='A')
        {
            scanf("DD %d %d %d\n",&x,&y,&z);
            makeroot(x);
            access(y);
            splay(0,y);
            int u=find(y);splay(1,u);
            add(u,z);
        }
        if(ch=='Q')
        {
            scanf("UERY %d %d\n",&x,&y);
            makeroot(x);
            access(y);
            splay(0,y);
            int u=find(y);splay(1,u);
            printf("%lld\n",sm[u]);
        }
        if(ch=='S')
        {
            scanf("HIFT %d %d\n",&x,&y);
            if(x==y) continue;
            makeroot(x);access(y);
            int u=find(x),v=find(y);
            splay(1,v);
            int lsv=a[1][v][0];
            fx[1][lsv]=fx[1][v],fx[0][fx[1][v]]=lsv,fx[1][v]=0;
            f[1][lsv]=0,a[1][v][0]=0;
            update(1,v);
            splay(1,u);
            f[1][v]=u,a[1][u][0]=v;
            update(1,u);
        }
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值