【例题】【动态树】

40 篇文章 0 订阅
22 篇文章 0 订阅

NKOJ3172 OTOCI
时间限制 : 50000 MS 空间限制 : 165536 KB

问题描述
给出n个结点以及每个点初始时对应的权值wi。起始时点与点之间没有连边。有3类操作:
1、bridge A B:询问结点A与结点B是否连通。如果是则输出“no”。否则输出“yes”,并且在结点A和结点B之间连一条无向边。
2、penguins A X:将结点A对应的权值wA修改为X。
3、excursion A B:如果结点A和结点B不连通,则输出“impossible”。否则输出结点A到结点B的路径上的点对应的权值的和。
给出q个操作,要求在线处理所有操作。
数据范围:1<=n<=30000, 1<=q<=300000, 0<=wi<=1000。

输入格式
第一行包含一个整数n(1<=n<=30000),表示节点的数目。
第二行包含n个整数,第i个整数表示第i个节点初始时对应的权值。
第三行包含一个整数q(1<=n<=300000),表示操作的数目。
以下q行,每行包含一个操作,操作的类别见题目描述。
任意时刻每个节点对应的权值都是1到1000的整数。

输出格式
输出所有bridge操作和excursion操作对应的输出,每个一行。

样例输入
样例1:
5
4 2 4 5 6
10
excursion 1 1
excursion 1 2
bridge 1 2
excursion 1 2
bridge 3 4
bridge 3 5
excursion 4 5
bridge 1 3
excursion 2 4
excursion 2 5

样例2:
6
1 2 3 4 5 6
10
bridge 1 2
bridge 2 3
bridge 4 5
excursion 1 3
excursion 1 5
bridge 3 4
excursion 1 5
penguins 3 10
excursion 1 3
bridge 1 5

样例输出
样例1:
4
impossible
yes
6
yes
yes
15
yes
15
16

样例2:
yes
yes
yes
6
impossible
yes
15
13
no

来源 Croatian Olympiad in Informatics 2009

static 思路:
*1、rotate+lazy(super_putdown)->splay->access->setroot(将某一节点置为实际根:拉通、旋转到splay的根、整颗splay反向)
2、判断是否联通:分别getroot(拉通、旋转到splay的根、找最左的儿子,即深度最小的)
*3、连接:link(某一节点变为它所在树的根,将它的爸爸置为另一节点)
4、修改权值:不能直接改,先置根,修改后要更新
5、询问路径权值:拉通某一节点(因为拉通是使该节点和实际根拉通,所以要先setroot另一节点),旋转到splay的根

#include<cstdio>
#include<iostream>
using namespace std;
const int need=30004;

//......................................
inline void inc(char &t)
{
    t=getchar();
    while(t==10||t==' ') t=getchar();
}
inline int in_()
{
    int d;char t=getchar();
    while(t<'0'||t>'9') t=getchar();
    for(d=0;!(t<'0'||t>'9');t=getchar()) d=(d<<1)+(d<<3)+t-'0';
    return d;
}
inline void out_(int x)
{
    if(x>=10) out_(x/10);
    putchar(x%10+'0');
}
//......................................
int ls[need],rs[need],fa[need],val[need],v[need],lazy[need];

#define isroot(x) ((!(ls[fa[x]]==x||rs[fa[x]]==x))||fa[x]==0)
#define NBHB(x) val[x]=val[ls[x]]+val[rs[x]]+v[x]

void putdown(int x)
{
    lazy[x]=0;
    swap(ls[x],rs[x]);
    if(ls[x]) lazy[ls[x]]^=1;
    if(rs[x]) lazy[rs[x]]^=1;
}
inline void sr(int x)
{
    int y=fa[x],z=fa[y];
    if(lazy[z]) putdown(z);
    if(lazy[y]) putdown(y);
    if(lazy[x]) putdown(x);
    if(!isroot(y)) ls[z]==y ? ls[z]=x :rs[z]=x; fa[x]=z;
    ls[y]=rs[x]; fa[rs[x]]=y;
    rs[x]=y; fa[y]=x;
    NBHB(y),NBHB(x);
}
inline void sl(int x)
{
    int y=fa[x],z=fa[y];
    if(lazy[z]) putdown(z);
    if(lazy[y]) putdown(y);
    if(lazy[x]) putdown(x);
    if(!isroot(y)) ls[z]==y ? ls[z]=x :rs[z]=x; fa[x]=z;
    rs[y]=ls[x]; fa[ls[x]]=y;
    ls[x]=y; fa[y]=x;
    NBHB(y),NBHB(x);
}

void super_putdown(int x)
{
    if(!isroot(x)) super_putdown(fa[x]);
    if(lazy[x]) putdown(x);
}

inline void splay(int x)
{
    super_putdown(x);
    int y;
    while(!isroot(x))
    {
        y=fa[x];
        if(ls[y]==x) sr(x);
        else sl(x);
    }
}

inline void access(int x) 
{
    for(int t=0;x;t=x,x=fa[x]) 
    {
         splay(x);
         rs[x]=t;
         NBHB(x);
    }

}
#define becomeroot(x) access(x),splay(x),lazy[x]^=1//,swap(ls[x],rs[x])
#define link(x,y) becomeroot(x),fa[x]=y
int getroot(int x)
{
    access(x),splay(x);
    while(ls[x]) x=ls[x];
    return x;
}

int main()
{
    int n;n=in_();
    for(int i=1;i<=n;i++) val[i]=v[i]=in_();
    int m=in_();char c;int a,b,x,y;
while(m--)
{
    inc(c);
    if(c=='b')
    {
        a=in_(),b=in_();
        x=getroot(a),y=getroot(b);
        if(x==y) putchar('n'),putchar('o'),putchar(10);
        else link(a,b),putchar('y'),putchar('e'),putchar('s'),putchar(10);
    }
    else if(c=='p')
    {
        a=in_(),b=in_();
        splay(a),v[a]=b,NBHB(a);
    }
    else if(c=='e')
    {
        a=in_(),b=in_();
        x=getroot(a),y=getroot(b);
        if(x!=y) puts("impossible");
        else 
        {
            becomeroot(a);
            access(b);
            splay(b);
            out_(val[b]),putchar(10);
        }
    }
}
}

2、
NKOJ2381 弹飞绵羊
时间限制 : 100000 MS 空间限制 : 265536 KB

问题描述
某天,Lostmonkey发明了一种超级弹力装置,为了在他的绵羊朋友面前显摆,他邀请小绵羊一起玩个游戏。游戏一开始,Lostmonkey在地上沿着一条直线摆上n个装置,每个装置设定初始弹力系数ki,当绵羊达到第i个装置时,它会往后弹ki步,达到第i+ki个装置,若不存在第i+ki个装置,则绵羊被弹飞。绵羊想知道当它从第i个装置起步时,被弹几次后会被弹飞。为了使得游戏更有趣,Lostmonkey可以修改某个弹力装置的弹力系数,任何时候弹力系数均为正整数。

输入格式
第一行包含一个整数n,表示地上有n个装置,装置的编号从0到n-1,
接下来一行有n个正整数,依次为那n个装置的初始弹力系数。
第三行有一个正整数m,接下来m行每行至少有两个数i、j,若i=1,你要输出从j出发被弹几次后被弹飞,若i=2则还会再输入一个正整数k,表示第j个弹力装置的系数被修改成k。
对于20%的数据n,m<=10000,对于100%的数据n<=200000,m<=100000

输出格式
对于每个i=1的情况,你都要输出一个需要的步数,占一行。

样例输入
4
1 2 1 1
3
1 1
2 1 1
1 1

样例输出
2
3

来源 HZOI

思路:
设置虚点到达虚点表示被弹飞,显然虚点应为实际根
注意到link或cut会改变实际根,所以每次询问弹飞次数时应先将root置为实际根,再操作

6、cut:将x置为实际根、将y加入并旋转到splay根、切断y左儿子(包括左儿子father置0)

#include<cstdio>
#include<iostream>
using namespace std;
const int need=200003;

//......................................
inline int in_()
{
    int d;char t=getchar();
    while(t<'0'||t>'9') t=getchar();
    for(d=0;!(t<'0'||t>'9');t=getchar()) d=(d<<1)+(d<<3)+t-'0';
    return d;
}
inline void out_(int x)
{
    if(x>=10) out_(x/10);
    putchar(x%10+'0');
}
//......................................
int root;
int ls[need],rs[need],fa[need],si[need],to[need];
bool lazy[need];

#define isroot(x) ((!(ls[fa[x]]==x||rs[fa[x]]==x))||fa[x]==0)
#define NBHB(x) si[x]=si[ls[x]]+si[rs[x]]+1
inline void putdown(int x)
{
    lazy[x]=0;
    swap(ls[x],rs[x]);
    if(ls[x]) lazy[ls[x]]^=1;
    if(rs[x]) lazy[rs[x]]^=1;
}
int sss[need],tops;
void super_putdown(int x)
{
    if(!isroot(x)) super_putdown(fa[x]);
    if(lazy[x]) putdown(x);
}
void rotate(int x,int mm)
{
    int y=fa[x],z=fa[y];
    if(lazy[z]) putdown(z);
    if(lazy[y]) putdown(y);
    if(lazy[x]) putdown(x);
    if(!isroot(y)) ls[z]==y ? ls[z]=x :rs[z]=x;
    fa[x]=z;
    if(mm==1)
    {
        ls[y]=rs[x]; fa[rs[x]]=y;
        rs[x]=y; fa[y]=x;
    }
    else 
    {
        rs[y]=ls[x]; fa[ls[x]]=y;
        ls[x]=y; fa[y]=x;
    }
    NBHB(y),NBHB(x);
}
void splay(int x)
{
    super_putdown(x);
    while(!isroot(x))
    {
        if(ls[fa[x]]==x) rotate(x,1);
        else rotate(x,2);
    }
}
//......................................
inline void join(int x)
{
    for(int t=0;x;t=x,x=fa[x]) 
     splay(x),rs[x]=t,NBHB(x);
}
#define setroot(x) join(x),splay(x),lazy[x]^=1
#define link(x,y) setroot(x),fa[x]=y
#define cut(x,y) setroot(x),join(y),splay(y),fa[ls[y]]=0,ls[y]=0,NBHB(y)
//......................................
int main()
{
    int n;n=in_();
    root=1+n;
    for(int i=1,t;i<=n;i++) 
    {
        t=to[i]=in_();
        t+=i;
        if(t>n) t=root;
        link(i,t);
        si[i]=1;
    }
    int m;m=in_();
    int a,j,k,t;
    for(int i=1;i<=m;i++)
    {
        a=in_(),j=in_();
        j++;
        if(a==1)
        {
            setroot(root);
            join(j);
            splay(j);
            out_(si[j]-1),putchar(10);
        }
        else
        {
            k=in_();
            t=to[j]+j;
            if(t>n) t=root;
            cut(j,t);
            t=(to[j]=k)+j;
            if(t>n) t=root;
            link(j,t);
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值