【GDSOI2017】中学生数据结构题

7 篇文章 0 订阅
7 篇文章 0 订阅

Description

这里写图片描述

Input

第一行有一个整数 N,表示 S 国城市的数量。
接下来有 N-1 行,每行两个数 u,v 表示一条道路。
第 N+1 行为一个整数 Q,表示接下来有 Q 个操作。
接下来有 Q 行,每行表示一个操作,格式如题目描述所示。

Output

对于每一个 QUERY 操作,输出一个数,表示询问的当前编号为 X 和编号为 Y 的城市的最短路径间的城市 (包括编号为 X 的城市和编号为 Y 的城市)的士兵总和。

Sample Input

Sample Input1:
5
1 2
1 3
3 4
3 5
6
ADD 1 5 2
ADD 3 4 1
QUERY 1 4
SHIFT 1 4
ADD 5 4 1
QUERY 4 5

Sample Input12:
5
1 2
2 3
3 4
4 5
5
ADD 1 3 2
ADD 3 5 1
SHIFT 2 4
QUERY 1 3
QUERY 1 5

Solution

GDOI必有一题数据结构防AK题
此题有两种解法

第一种

(我不是用的这种)
这种方法思考和实现起来比较简单,细节也比较少,但是码量比第二种大,大概5000~6000Byte

用树链剖分和splay来维护,对于每一个重链和每一个轻边建一颗splay
add和query很简单,用常规的树链剖分做法做就行了
shift操作在两颗splay中间时要注意,把前面的最后一个点接到后面的第一个点之前,再把最前面和最后面的splay维护一下即可

第二种

(我用的是这种方法)
用LCT维护
shift操作时要断点和连点,所以splay会被破坏,所以要两棵LCT
一棵是编号LCT,一棵是权值LCT
在makeroot,access等时,编号LCT的编号是固定的,可直接使用
在access的时候,splay的形态会改变,同时要保证两棵LCT形态完全相同
在add,shift,query时,找到编号LCT中某个点对应在权值LCT中的点,然后求值
那么怎样找编号LCT中某个点对应的权值LCT中的点呢?
需要记录下编号LCT中的每棵splay的根对应的权值LCT中的每棵splay的根是哪一个,反过来也需要记录下来,这个只用在access和rotate的时候维护
因为两棵LCT形态相同,所以中序遍历的点是相同的
所有找对应点时,利用编号LCT中点的位置,通过中序遍历找到权值LCT对应的点
然后就愉快的码吧

Code

#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 101000
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define ll long long
using namespace std;
int p[N],fa[2][N],t[2][N][2],rev[2][N],n,root[2][N],s[N];
ll lz[N],sum[N],d[N],size[2][N];
int lr(int qq,int x){return x==t[qq][fa[qq][x]][1];}
void down(int qq,int x)
{
    if(rev[qq][x])
    {
        swap(t[qq][x][0],t[qq][x][1]);
        rev[qq][t[qq][x][0]]^=1;rev[qq][t[qq][x][1]]^=1;
        rev[qq][x]=0;
    }
    if(qq==1)
    {
        if(lz[x])
        {
            if(t[1][x][0]) d[t[1][x][0]]+=lz[x],sum[t[1][x][0]]+=lz[x]*size[1][t[1][x][0]],lz[t[1][x][0]]+=lz[x];
            if(t[1][x][1]) d[t[1][x][1]]+=lz[x],sum[t[1][x][1]]+=lz[x]*size[1][t[1][x][1]],lz[t[1][x][1]]+=lz[x];
            lz[x]=0;
        }
    }
}
int kth(int r,int x)
{
    down(1,r);
    if(x==size[1][t[1][r][0]]+1) return r;
    if(x<=size[1][t[1][r][0]]) return kth(t[1][r][0],x);
    else return kth(t[1][r][1],x-size[1][t[1][r][0]]-1);
}
void update(int qq,int x)
{
    size[qq][x]=size[qq][t[qq][x][0]]+size[qq][t[qq][x][1]]+1;
    if(qq==1) sum[x]=sum[t[1][x][0]]+sum[t[1][x][1]]+d[x];
}
void rotate(int qq,int x)
{
    int y=fa[qq][x],k=lr(qq,x);
    t[qq][y][k]=t[qq][x][1-k];if(t[qq][x][1-k]) fa[qq][t[qq][x][1-k]]=y;
    fa[qq][x]=fa[qq][y];
    if(fa[qq][y]) t[qq][fa[qq][y]][lr(qq,y)]=x;
    else 
    {
        root[qq][x]=root[qq][y];root[1-qq][root[qq][y]]=x;
        if(qq==0) p[x]=p[y],p[y]=0;
    }
    fa[qq][y]=x;t[qq][x][1-k]=y;
    update(qq,y);update(qq,x);
}
void xc(int qq,int x)
{
    for(;x;x=fa[qq][x]) s[++s[0]]=x;
    for(;s[0];s[0]--) down(qq,s[s[0]]);
}
void splay(int qq,int x,int y)
{
    xc(qq,x);
    while(fa[qq][x]!=y)
    {
        if(fa[qq][fa[qq][x]]!=y)
            if(lr(qq,x)==lr(qq,fa[qq][x])) rotate(qq,fa[qq][x]);
            else rotate(qq,x);
        rotate(qq,x);
    }
}
int get(int x)
{
    splay(1,root[0][x],0);
    return kth(root[0][x],size[0][t[0][x][0]]+1);
}
void access(int x)
{
    int y=0,yy=0;
    while(x>0)
    {
        splay(0,x,0);
        int xx=get(x);
        splay(1,xx,0);
        fa[0][t[0][x][1]]=0;p[t[0][x][1]]=x;
        fa[1][t[1][xx][1]]=0;
        root[0][t[0][x][1]]=t[1][xx][1];
        root[1][t[1][xx][1]]=t[0][x][1];
        t[0][x][1]=y;
        t[1][xx][1]=yy;
        fa[0][y]=x;p[y]=0;
        fa[1][yy]=xx;
        update(0,x);update(1,xx);
        y=x;x=p[x];yy=xx;
    }
}
void makeroot(int x)
{
    access(x);
    splay(0,x,0);
    rev[0][x]^=1;
    int xx=get(x);
    splay(1,xx,0);
    rev[1][xx]^=1;
}
void link(int x,int y)
{
    makeroot(x);p[x]=y;
}
int main()
{
    freopen("shift.in","r",stdin);
    freopen("shift.out","w",stdout);
    scanf("%d",&n);
    fo(i,1,n) root[0][i]=root[1][i]=i;
    fo(i,1,n-1)
    {
        int x,y;scanf("%d%d",&x,&y);
        link(x,y);
    }
    int ac;scanf("%d\n",&ac);
    for(;ac;ac--)
    {
        char ch;scanf("%c",&ch);
        int x,y;ll z;
        if(ch=='A')
        {
            scanf("DD%d%d%d\n",&x,&y,&z);
            makeroot(x);
            access(y);
            splay(0,x,0);
            int x1=get(x);
            splay(1,x1,0);
            d[x1]+=z;sum[x1]+=z*size[1][x1];lz[x1]+=z;
        }
        if(ch=='S')
        {
            scanf("HIFT%d%d\n",&x,&y);
            if(x==y) continue;
            makeroot(x);access(y);
            splay(0,x,0);
            int xx=get(x);
            splay(0,y,0);
            int yy=get(y);
            splay(1,yy,0);fa[1][t[1][yy][0]]=0,t[1][yy][0]=0;
            splay(1,xx,0);fa[1][t[1][yy][1]=xx]=yy,root[0][y]=yy;
        }
        if(ch=='Q')
        {
            scanf("UERY%d%d\n",&x,&y);
            makeroot(x);access(y);
            splay(0,x,0);
            int x1=get(x);
            splay(1,x1,0);
            printf("%lld\n",sum[x1]);
        }
    }
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值