[HNOI2010]弹飞绵羊

#题目链接
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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值