1. 程式人生 > >【例題】【動態樹】

【例題】【動態樹】

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);
        }
    }
}