P3203 [HNOI2010]弹飞绵羊

本文详细解析了HNOI2010竞赛中“弹飞绵羊”问题的解题思路,利用LCT(Link-Cut Tree)数据结构进行高效操作,包括代码实现细节,如旋转、切边、链接等关键函数。

P3203 [HNOI2010]弹飞绵羊

思路

每个点都往后面连边
所以肯定没有环
超过n的算连向n+1
n+1个点,n条边,没有环,一定联通
那就差不多是个树了
然后就LCT模拟他的操作就行
有比较简单的LCT做法,不过我还是喜欢无脑一点的

错误

cut操作x写错i

代码

#include <bits/stdc++.h>
#define ls ch[x][0]
#define rs ch[x][1]
#define FOR(i,a,b) for(int i=a;i<=b;++i)
using namespace std;
const int N=2e5+7;
int read() {
    int x=0,f=1;char s=getchar();
    for(;s>'9'||s<'0';s=getchar()) if(s=='-') f=-1;
    for(;s>='0'&&s<='9';s=getchar()) x=x*10+s-'0';
    return x*f;        
}
int a[N],f[N],ch[N][2],s[N],lazy[N];
void pushup(int x) {s[x]=s[ls]+s[rs]+1;}
void tag(int x) {swap(ls,rs);lazy[x]^=1;}
bool isroot(int x) {return ch[f[x]][0]==x || ch[f[x]][1]==x;}
void pushdown(int x) {
    if(lazy[x]) {
        if(ls) tag(ls);
        if(rs) tag(rs);
        lazy[x]=0;
    }
}
void rotate(int x) {
    int y=f[x],z=f[y],k=ch[y][1]==x,m=ch[x][k^1];
    if(isroot(y)) ch[z][ch[z][1]==y]=x;
    ch[x][k^1]=y;
    ch[y][k]=m;
    if(m) f[m]=y;
    f[x]=z;
    f[y]=x;
    pushup(y);
}
int Q[N];
void splay(int x) {
    int y=x,z=0;
    Q[++z]=y;
    while(isroot(y)) Q[++z]=y=f[y];
    while(z) pushdown(Q[z--]);
    while(isroot(x)) {
        y=f[x],z=f[y];
        if(isroot(y)) (ch[y][0]==x)^(ch[z][0]==y) ? rotate(x) : rotate(y);
        rotate(x);
    }
    pushup(x);
}
void access(int x) {
    for(int y=0;x;y=x,x=f[x])
        splay(x),rs=y,pushup(x);
}
void makeroot(int x) {
    access(x);
    splay(x);
    tag(x);
}
int findroot(int x) {
    access(x);
    splay(x);
    while(ls) pushdown(x),x=ls;
    return x; 
}
void split(int x,int y) {
    makeroot(x);
    access(y);
    splay(y);
}
void link(int x,int y) {
    makeroot(x);
    if(findroot(y)!=x) f[x]=y;
}
void cut(int x,int y) {
    makeroot(x);
    if(findroot(y)==x&&f[x]==y&&!ch[x][1]) {
        f[x]=ch[y][0]=0;
        pushup(y);
    }
}
int main() {
    int n=read();
    FOR(i,1,n+1) s[i]=1;
    FOR(i,1,n) a[i]=read();
    FOR(i,1,n) {
        link(i,(i+a[i])>n ? n+1 : i+a[i]);
    }
    int m=read();
    FOR(i,1,m) {
        int opt=read(),x=read()+1;
        if(opt==1) {
            split(x,n+1);
            cout<<s[n+1]-1<<"\n";
        } else {
            int y=read();
            cut(x,(a[x]+x)>n ? n+1 : a[x]+x);
            a[x]=y;
            link(x,(a[x]+x)>n ? n+1 : a[x]+x);
        }
    }
    return 0;
}

转载于:https://www.cnblogs.com/dsrdsr/p/10181496.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值