【GDSOI2017】 中学生数据结构题

3 篇文章 0 订阅
1 篇文章 0 订阅

Description

这里写图片描述

Data Constraint

这里写图片描述

Solution

看到SHIFT操作,很显然就想到了splay中的翻转操作,还在树上,我们就考虑LCT。
但想了一会我们发现,直接SHIFT操作会破坏LCT树中的点与点的连接关系。
于是我们考虑维护两颗LCT,一颗维护树的形态,一颗维护树中的权值,保证对于一个维护树的形态的LCT中的splay,维护树中权值的LCT中都有一个大小相同的与之对应的splay,且这两个splay的中序遍历恰好使得每个编号都对应回自己的权值。对于一个SHIFT,我们就只翻转权值树中的点,这样就保证了在翻转同时不破坏树的形态。
那问题又来了,我们如何维护这两个LCT树之间的关系?对于一个编号,我们如何知道他在权值LCT中的位置呢?
对于每个操作,如access,makeroot等,我们可以同时对这两个LCT进行操作。那么我们对于一个编号,由于保证了对于一个编号splay的中序遍历,都有一个权值splay中的中序遍历与之相同,我们可以维护这个编号splay在权值LCT中的splay是哪个,然后我们按照当前编号在编号LCT中的splay的中序遍历的位置x,在对应的权值LCT中按中序遍历找一下第x个就好了。

Code

#include<iostream>
#include<cmath>
#include<cstring>
#include<cstdio>
#include<algorithm>
#define ll long long
using namespace std;
const int maxn=2e5+5;
int fa[maxn],pfa[maxn],f[maxn][2],bz[maxn],bz3[maxn],size[maxn],p[maxn];
int first[maxn],last[maxn],next[maxn],fa1[maxn],g[maxn][2],bz1[maxn];
int n,m,i,t,j,k,l,x,y,num,xx,yy,d[maxn],bz4[maxn];
ll sum[maxn],bz2[maxn],size1[maxn],b[maxn],z;
char s[20];
void bfs(){
    int i=0,j=1,x;d[1]=1;
    while (i<j){
        x=d[++i];size1[x]=size[x]=1;p[x]=x;
        for (t=first[x];t;t=next[t])
            if (!size[last[t]]) d[++j]=last[t],pfa[d[j]]=x;
    }
}
void change1(int x){
    int y=g[x][0],z=g[x][1];
    if (bz1[x]) swap(g[y][0],g[y][1]),swap(g[z][0],g[z][1]),bz1[y]=1-bz1[y],bz1[z]=1-bz1[z],bz1[x]=0;
    if (bz2[x]) b[y]+=bz2[x],b[z]+=bz2[x],sum[y]+=size1[y]*bz2[x],sum[z]+=size1[z]*bz2[x],bz2[y]+=bz2[x],bz2[z]+=bz2[x],bz2[x]=0;
}
bool son(int x){return f[fa[x]][1]==x;}
bool son1(int x){return g[fa1[x]][1]==x;}
void change(int x){
    int y=f[x][0],z=f[x][1];
    if (bz[x]) swap(f[y][0],f[y][1]),swap(f[z][0],f[z][1]),bz[y]=1-bz[y],bz[z]=1-bz[z],bz[x]=0;
    if (bz3[x]) p[y]=bz3[y]=p[z]=bz3[z]=bz3[x],bz3[x]=0;
}
void move(int x,int y){
    while (x!=y) d[++d[0]]=x,x=fa[x];
    for (;d[0];d[0]--) change(d[d[0]]);
}
void move1(int x,int y){
    while (x!=y) d[++d[0]]=x,x=fa1[x];
    for (;d[0];d[0]--) change1(d[d[0]]);
}
void rotate(int x){
    size[x]=size[f[x][0]]+size[f[x][1]]+1;
}
void rotate1(int x){
    int y=g[x][0],z=g[x][1];size1[x]=size1[y]+size1[z]+1;sum[x]=sum[y]+sum[z]+b[x];
}
void make(int x){
    int y=fa[x],z=son(x);
    f[fa[y]][son(y)]=x;fa[x]=fa[y];swap(pfa[x],pfa[y]);
    fa[f[y][z]=f[x][1-z]]=y;fa[f[x][1-z]=y]=x;rotate(y);rotate(x);
}
void make1(int x){
    int y=fa1[x],z=son1(x);
    g[fa1[y]][son1(y)]=x;fa1[x]=fa1[y];
    fa1[g[y][z]=g[x][1-z]]=y;fa1[g[x][1-z]=y]=x;rotate1(y);rotate1(x);
}
int splay(int x,int y){
    move(x,y);
    while (fa[x]!=y){
        if (fa[fa[x]]!=y)
            if (son(x)==son(fa[x])) make(fa[x]);
            else make(x);
        make(x);
    }
}
int splay1(int x,int y){
    move1(x,y);
    while (fa1[x]!=y){
        if (fa1[fa1[x]]!=y)
            if (son1(x)==son1(fa[x])) make1(fa1[x]);
            else make1(x);
        make1(x);
    }
}
int find2(int x,int sum){
    change1(x);
    while (sum!=size1[g[x][0]]+1){
        if (size1[g[x][0]]+1<sum) sum-=size1[g[x][0]]+1,x=g[x][1];
        else x=g[x][0];
        change1(x);
    }
    return x;
}
int find(int x){
    splay(x,0);
    return find2(p[x],size[f[x][0]]+1);
}
void access(int x,int xx){
    int y=0,yy=0;splay(x,0);
    while (x){
        splay1(xx,0);
        fa[f[x][1]]=0;pfa[f[x][1]]=x;p[f[x][1]]=bz3[f[x][1]]=g[xx][1];fa[f[x][1]=y]=x;pfa[y]=0;rotate(x);
        fa1[g[xx][1]]=0;fa1[g[xx][1]=yy]=xx;rotate1(xx);
        y=x,x=pfa[x];yy=xx;if (x)xx=find(x);
    }
}
void makeroot(int xx,int x){
    access(x,xx);splay(x,0);
    bz[x]=1-bz[x];swap(f[x][0],f[x][1]);splay1(xx,0);bz1[xx]=1-bz1[xx];swap(g[xx][0],g[xx][1]);p[x]=bz3[x]=xx;
}
void lian(int x,int y){
    last[++num]=y;next[num]=first[x];first[x]=num;
}
int main(){
    freopen("shift.in","r",stdin);freopen("shift.out","w",stdout);
    scanf("%d",&n);
    for (i=1;i<n;i++)
        scanf("%d%d",&x,&y),lian(x,y),lian(y,x);
    bfs();
    scanf("%d\n",&m);
    for (i=1;i<=m;i++){
        scanf("%s%d%d",s+1,&x,&y);
        xx=find(x);yy=find(y);
        if (s[1]=='A'){
            scanf("%lld",&z);
            makeroot(xx,x);
            access(y,yy);
            splay1(xx,0);splay(x,0);p[x]=bz3[x]=xx;
            bz2[xx]+=z;sum[xx]+=z*size1[xx];b[xx]+=z;
        }else if (s[1]=='Q'){
            makeroot(xx,x);
            access(y,yy);splay1(xx,0);splay(x,0);
            printf("%lld\n",sum[xx]);p[x]=bz3[x]=xx;
        }else{
            makeroot(xx,x);access(y,yy);splay1(yy,0);swap(g[yy][0],g[yy][1]);splay(y,0);p[y]=bz3[y]=yy;
        }
        scanf("\n");
    }
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值