bzoj2002 [Hnoi2010]Bounce 弹飞绵羊
原题地址:http://www.lydsy.com:808/JudgeOnline/problem.php?id=2002
题意:
在地上沿着一条直线摆上n个装置,每个装置设定初始弹力系数ki,当绵羊达到第i个装置时,它会往后弹ki步,达到第i+ki个装置,若不存在第i+ki个装置,则绵羊被弹飞。
绵羊想知道当它从第i个装置起步时,被弹几次后会被弹飞。
有m个操作,每次可以修改某个弹力装置的弹力系数,任何时候弹力系数均为正整数,或者查询从某个点开始弹几次会被弹飞。
数据范围
n<=200000,m<=100000
题解:
因为每个点只会向后连接一个点,所以最后形成的是一个森林。
如果默认编号更大的点更浅,那么查询一个点弹几次会被弹飞,就是查询这个点到它的根的深度。
那么只需要支持更改父节点的操作和查询点的深度的操作。
就是Link和Cut。
查询深度只需access然后就会有根节点到该点的路径,查询根的size即可。
代码:
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=200005;
int n,m,k[N];
struct node
{
int fa,ch[2],size;
bool rez;
void init()
{
fa=ch[0]=ch[1]=0; size=1;
rez=0;
}
}tr[N];
struct Link_Cut_Tree
{
bool isroot(int x)
{
return (tr[tr[x].fa].ch[0]!=x)&&(tr[tr[x].fa].ch[1]!=x);
}
void update(int x)
{
tr[x].size=1; int ls=tr[x].ch[0]; int rs=tr[x].ch[1];
if(ls) tr[x].size+=tr[ls].size;
if(rs) tr[x].size+=tr[rs].size;
return;
}
void rotate(int x)
{
int y=tr[x].fa; int z=tr[y].fa;
if(!isroot(y))
{
if(tr[z].ch[0]==y) tr[z].ch[0]=x; else tr[z].ch[1]=x;
}
tr[x].fa=z;
int l= tr[y].ch[0]==x? 0:1; int r=l^1;
tr[y].ch[l]=tr[x].ch[r]; tr[tr[x].ch[r]].fa=y;
tr[x].ch[r]=y; tr[y].fa=x;
update(y); update(x);
}
void splay(int x)
{
while(!isroot(x))
{
int y=tr[x].fa; int z=tr[y].fa;
if(!isroot(y))
{
if(tr[y].ch[0]==x^tr[z].ch[0]==y) rotate(x);
else rotate(y);
}
rotate(x);
}
}
void access(int x)
{
int y=0;
for(;x;y=x,x=tr[x].fa)
{
splay(x);
tr[x].ch[1]=y;
update(x);
}
}
int findroot(int x)
{
access(x);splay(x);
while(tr[x].ch[0]) x=tr[x].ch[0];
return x;
}
void link(int u,int v)
{
if(u>v) swap(u,v);
if(findroot(u)==findroot(v)) return;
access(u); splay(u);
tr[u].fa=v;
}
void cut(int u,int v)
{
if(u>v) swap(u,v);
if(findroot(u)!=findroot(v)) return;
access(u); splay(u);
tr[v].fa=tr[u].ch[0]=0;
update(v); update(u);
return;
}
int query(int x)
{
access(x); splay(x);
return tr[x].size;
}
}LCT;
int main()
{
scanf("%d",&n);
for(int i=0;i<=n;i++) tr[i].init();
for(int i=1;i<=n;i++)
{
scanf("%d",&k[i]);
int j=i+k[i];
if(j<=n) LCT.link(i,j);
}
scanf("%d",&m);
while(m--)
{
int opt,x,y;
scanf("%d",&opt);
if(opt==1)
{
scanf("%d",&x); x++;
printf("%d\n",LCT.query(x));
}
else
{
scanf("%d%d",&x,&y);
x++;
if(x+k[x]<=n)
LCT.cut(x,x+k[x]);
k[x]=y;
if(x+k[x]<=n)
LCT.link(x,x+k[x]);
}
}
return 0;
}

本文介绍了一道名为“Bounce弹飞绵羊”的算法题目,通过构建森林数据结构来解决绵羊弹跳路径的问题。使用Link-Cut树进行高效查询和更新,支持修改弹力系数及查询弹飞次数。

被折叠的 条评论
为什么被折叠?



