【例題】【動態樹】
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);
}
}
}