传送门
题目描述:
Lostmonkey 发明了一种超级反弹装置。为了在绵羊朋友面前显摆,他邀请小绵羊一起玩个游戏。游戏一开始,Lostmonkey 在地上沿一条直线摆放 n n n 个反弹装置,并按从前往后的方式将反弹装置依次编号为 0 0 0 到 n − 1 n-1 n−1,对 0 ≤ i ≤ n − 1 0≤i≤n-1 0≤i≤n−1,为第 i i i 个反弹装置设定了初始弹力系数 k i k_i ki,当绵羊落到第 i i i 个反弹装置上时,它将被往后弹出 k i k_i ki 步,即落到第 i + k i i+k_i i+ki 个反弹装置上,若不存在第 i + k i i+k_i i+ki 个反弹装置,则绵羊被弹飞。绵羊想知道:从第 i i i 个反弹装置开始,它被弹出几次(含被弹飞的那次)后会被弹飞。为使游戏更有趣,Lostmonkey 还可以修改某个反弹装置的弹力系数,但任何时候弹力系数均为正整数。
输入格式:
输入第一行是一个整数 n n n ,表示地上摆放 n n n 个反弹装置。
输入第二行是用空格隔开的 n n n 个正整数 k 0 k_0 k0, k 1 k_1 k1, ⋯ ⋯ ⋯, k n − 1 k_{n-1} kn−1,分别表示 n n n 个反弹装置的初始弹力系数。
输入第三行是一个正整数 m m m,表示后面还有 m m m 行输入数据。
接下来的 m m m 行,每行至少有用空格隔开的两个整数 i i i 和 j j j ,若 i = 1 i=1 i=1,则你要输出从第 j j j 个反弹装置开始,被弹出几次后会被弹飞;若 i = 2 i=2 i=2,则该行有用空格隔开的三个整数 i i i, j j j 和 k k k,表示第 j j j 个反弹装置的弹力系数被修改为 k k k。
输出格式:
输出包含的行数等于输入文件最后 m m m 行中 i = 1 i=1 i=1 的行数。第 h h h 行输出一个整数,表示对输入中给出的第 h h h 个求弹出次数的问题,基于 n n n 个反弹装置当时的弹力系数,求出的弹出次数。
样例数据:
输入
4
1 2 1 1
3
1 1
2 1 1
1 1
输出
2
3
备注:
【数据范围】
20 % 20\% 20% 的数据满足: n , m ≤ 10000 n,m≤10000 n,m≤10000;
100 % 100\% 100% 的数据满足: n ≤ 200000 n≤200000 n≤200000, m ≤ 100000 m≤100000 m≤100000。
LCT 的模板题。
为了方便,我把下标编号加 1 1 1,即将下标编号变为 1 1 1 到 n n n。
对于 i i i,如果 i + k i ≤ n i+k_i\le n i+ki≤n,就连一条 ( i , i + k i ) (i,i+k_i) (i,i+ki) 的边,表示从 i i i 可以弹到 i + k i i+k_i i+ki;否则就连 ( i , n + 1 ) (i,n+1) (i,n+1) 的边,表示从 i i i 可以弹出去。
这样连出来后必然是一棵树,因为虽然每个点可以连进来很多条,但只能连出去一条。
修改操作很简单,先 Cut 掉原来连的边,再连上以当前弹力系数对应的边就行了。
然后是询问操作,我们先把 n + 1 n+1 n+1 变成这棵树的跟(Makeroot 操作),然后我们把 x x x 到根的路径提取出来(Access 操作),再把 x x x 转到辅助树的根(Splay 操作),那么最后 s i z e x − 1 size_x-1 sizex−1 就是答案了。
应该还是比较好懂的吧。。
#include
#include
#include
#include
#define N 200005
using namespace std;
int n,m,a[N],fa[N],son[N][2],mark[N],Size[N];
stack<int>stk;
int Get(int x)
{
return x==son[fa[x]][1];
}
bool Isroot(int x)
{
if(!fa[x]) return true;
return (son[fa[x]][0]!=x)&&(son[fa[x]][1]!=x);
}
void Pushup(int x)
{
Size[x]=Size[son[x][0]]+Size[son[x][1]]+1;
}
void Pushdown(int x)
{
if(!mark[x]) return;
if(son[x][0]) mark[son[x][0]]^=1;
if(son[x][1]) mark[son[x][1]]^=1;
swap(son[x][0],son[x][1]),mark[x]=0;
}
void Rotate(int x)
{
int y=fa[x],z=fa[y];
int k=Get(x),l=son[x][k^1];
son[y][k]=l;fa[l]=(l?y:0);
if(!Isroot(y)) son[z][Get(y)]=x;fa[x]=z;
son[x][k^1]=y,fa[y]=x;
Pushup(y),Pushup(x);
}
void Splay(int x)
{
stk.push(x);
for(int i=x;!Isroot(i);i=fa[i]) stk.push(fa[i]);
while(!stk.empty()) Pushdown(stk.top()),stk.pop();
while(!Isroot(x))
{
int y=fa[x];
if(!Isroot(y)) Rotate(Get(x)==Get(y)?y:x);
Rotate(x);
}
}
void Access(int x)
{
int i;
for(i=0;x;x=fa[i=x])
Splay(x),son[x][1]=i,Pushup(x);
}
void Makeroot(int x)
{
Access(x);
Splay(x);
mark[x]^=1;
}
void Link(int x,int y)
{
Makeroot(x);
fa[x]=y;
}
void Cut(int x,int y)
{
Makeroot(x);
Access(y);Splay(y);
son[y][0]=fa[x]=0;
Pushup(y);
}
int Query(int x)
{
Makeroot(n+1);
Access(x),Splay(x);
return Size[x]-1;
}
void Modify(int x,int y)
{
Cut(x,(x+a[x]<=n)?x+a[x]:n+1);
Link(x,(x+y<=n)?x+y:n+1),a[x]=y;
}
int main()
{
int x,y,i,op;
scanf("%d",&n);
for(i=1;i<=n;++i)
{
scanf("%d",&a[i]);
Link(i,(i+a[i]<=n)?i+a[i]:n+1);
}
scanf("%d",&m);
for(i=1;i<=m;++i)
{
scanf("%d%d",&op,&x);
if(op==1) printf("%d\n",Query(++x));
if(op==2) scanf("%d",&y),Modify(++x,y);
}
return 0;
}