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