先吐槽一句,这题分块比LCT快是什么鬼,LCT常数是有多大。LCT1.8s,分块总耗时1.6s。
主要思路:link(i,i+a[i]);弹出去的都连向节点n+1;
每次修改cut,link。查询是 以n+1为根,access(x),splay(x),x的子树大小,就是x点向后弹的节点数。
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<stack>
#define maxn 200005
using namespace std;
int n;
inline void read(int &a)
{
a=0;char c=getchar();
while(c<'0'||c>'9')
c=getchar();
while(c>='0'&&c<='9')
{
a*=10;a+=c-'0';
c=getchar();
}
}
int fa[maxn],ch[maxn][2],sz[maxn];
bool rev[maxn];
inline void up(int x)
{sz[x]=sz[ch[x][0]]+sz[ch[x][1]]+1;}
inline bool isroot(int x)
{return ch[fa[x]][0]!=x&&ch[fa[x]][1]!=x;}
inline void down(int x)
{
if(rev[x])
{
rev[ch[x][0]]^=1;
rev[ch[x][1]]^=1;
swap(ch[x][0],ch[x][1]);
rev[x]=0;
}
}
inline bool dir(int x)
{return x==ch[fa[x]][1];}
inline void rotate(int x)
{
int y=fa[x],z=fa[y];
bool b=dir(x);
int a=ch[x][!b];
if(!isroot(y))
ch[z][dir(y)]=x;
fa[x]=z;fa[y]=x;
ch[x][!b]=y;
ch[y][b]=a;
if(a) fa[a]=y;
up(y);up(x);
}
stack<int> S;
inline void splay(int x)
{
S.push(x);
for(int i=x;!isroot(i);i=fa[i])
S.push(fa[i]);
while(!S.empty())
{
int v=S.top();
S.pop();down(v);
}
while(!isroot(x))
{
int y=fa[x];
if(isroot(y)) rotate(x);
else
{
bool b=dir(x),c=dir(y);
if(b^c) {rotate(x);rotate(x);}
else {rotate(y);rotate(x);}
}
}
}
inline void access(int x)
{
int t=0;
while(x){
splay(x);ch[x][1]=t;up(x);
t=x;x=fa[x];
}
}
inline void make_root(int x)
{access(x);splay(x);rev[x]^=1;}
inline void link(int u,int v)
{make_root(u);fa[u]=v;}
inline void cut(int x,int y)
{
make_root(x);access(y);splay(y);
ch[y][0]=fa[x]=0,up(y);
}
inline int find(int x)
{
access(x);splay(x);
while(ch[x][0]) x=ch[x][0];
return x;
}
int a[maxn];
int main()
{
read(n);
int op,x,y;
for(int i=1;i<=n;i++)
{
read(a[i]);
link(i,min(i+a[i],n+1));
}
int q;read(q);
for(int i=1;i<=q;i++)
{
read(op);read(x);
x++;
if(op==1)
{
make_root(n+1);
access(x);splay(x);
printf("%d\n",sz[x]-1);
}
if(op==2)
{
read(y);cut(x,min(n+1,x+a[x]));
link(x,min(n+1,x+y));
a[x]=y;
}
}
}