#题目链接
https://www.luogu.org/problemnew/show/P3203
#做法
一开始看这道题,我先把它模型转换了一下每个点向它被弹向的那个点连边,如果被弹飞了就向0号点连边,那么我们会得到一个n+1个点(因为还有0号点),n条边的连通图,显然这是一颗树,那么对应的询问操作就是查询到根的距离,对应的修改操作就是把一颗子树移动到另一个节点下面,好了,LCT模板题。
但是经过同学的指点,我们发现这道题不需要用LCT,直接大力分一波块。我们预处理出每个点跳到下一个块所需要的步数,和它跳过去落在的位置,这样我们查询的时候暴力的一步一步向后跳就行了,复杂度是O(块的数量)。而每次修改,我们注意到这个修改节点的后面的点是不会变的,而前面的点会变,然后我们先修改这个点,然后对于它前面的点暴力的和一开始预处理一样修改就行了,同时我们注意到,不是这个块的节点一定不需要修改。
具体的实现看代码
// luogu-judger-enable-o2
#include<cstdio>
#include<algorithm>
#include<cctype>
#include<cstring>
#include<iostream>
#include<cmath>
#define LL long long
#define INF (2139062143)
#define N (200001)
using namespace std;
int n,m,opt,x,y,unit;
int a[N],blo[N];
struct node{
int tot,po;
}kk[N];
template <typename T> void read(T&t) {
t=0;
bool fl=true;
char p=getchar();
while (!isdigit(p)) {
if (p=='-') fl=false;
p=getchar();
}
do {
(t*=10)+=p-48;p=getchar();
}while (isdigit(p));
if (!fl) t=-t;
}
int main(){
//freopen("testdata.in","r",stdin);
//freopen("a.out","w",stdout);
read(n);
for (int i=1;i<=n;i++){
read(a[i]);
}
unit=sqrt(n);
for (int i=1;i<=n;i++){
blo[i]=(i-1)/unit+1;
}
for (int i=n;i>=1;i--){
if (i+a[i]>n){
kk[i].tot=1,kk[i].po=0;
continue;
}
if (blo[i]!=blo[i+a[i]]){
kk[i].tot=1,kk[i].po=i+a[i];
}
else{
kk[i].tot=kk[i+a[i]].tot+1,kk[i].po=kk[i+a[i]].po;
}
}
read(m);
while (m--){
read(opt);
if (opt==1){
read(x);
x++;
int ans=0;
while (x!=0){
ans+=kk[x].tot;
x=kk[x].po;
}
printf("%d\n",ans);
}
else{
read(x),read(y);
x++;
if (blo[x]!=blo[x+y]){
kk[x].tot=1,kk[x].po=x+y;
}
else kk[x].tot=kk[x+y].tot+1,kk[x].po=kk[x+y].po;
a[x]=y;
for (int i=x-1;blo[i]==blo[x];i--){
if (i+a[i]>x) continue;
kk[i].tot=kk[i+a[i]].tot+1,kk[i].po=kk[i+a[i]].po;
}
}
}
return 0;
}