2002: [Hnoi2010]Bounce 弹飞绵羊

题目链接

题目大意:维护数列,a[x]表示从x出发会到达x+a[x],支持两种操作
1.单点修改 2.查询从x出发经过几次会弹飞(坐标超过n)

题解:两种解法,LCT|分块

分块:
>O(1)O(n)
()>O(n)O(1)

2姿
就可以YY出分块方法了
>O(n)O(n)

具体见代码

强啊

LCT:加一个根为n+1,x+a[x]为x的父亲,弹飞的点父亲为根,修改需要断边+连新边,查询则是把n+1转到根,再把x到根的路径提取,由于辅助树中splay按照深度关键字排序,根的左子树大小就是要被弹的次数

虽然分块复杂度高,但是跑的比LCT快……应该是我写的太挫了……

我的收获:可以多维护几个信息……常用的建树思想……

分块

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std;

const int M=200005;

int n,m,blo,num;
int a[M],stp[M],nxt[M],pos[M];

int query(int x){
    int ret=0;
    while(nxt[x]) ret+=stp[x],x=nxt[x];
    return ret;
}

void updata(int x,int v)
{
    a[x]=v;int l=(pos[x]-1)*blo+1;
    for(int i=x;i>=l;i--)//类似初始化 
    if(pos[i]==pos[i+a[i]]) stp[i]=stp[i+a[i]]+1,nxt[i]=nxt[i+a[i]];
    else stp[i]=1,nxt[i]=i+a[i]; 
}

void work()
{
    int opt,x,y;
    while(m--){
        scanf("%d%d",&opt,&x);x++;
        if(opt==1) printf("%d\n",query(x));
        if(opt==2) scanf("%d",&y),updata(x,y);
    }
}

void init()
{
    cin>>n;blo=sqrt(n);num=(n-1)/blo+1;
    for(int i=1;i<=n;i++) scanf("%d",&a[i]),pos[i]=(i-1)/blo+1;
    for(int i=n;i>0;i--){//逆序更新,因为前面的信息使用了后面的信息维护 
        if(i+a[i]>n) stp[i]=1,nxt[i]=n+1;//弹飞 
        else if(pos[i]==pos[i+a[i]]) stp[i]=stp[i+a[i]]+1,nxt[i]=nxt[i+a[i]];//块内 
        else stp[i]=1,nxt[i]=i+a[i];//块外 
    }
    cin>>m;
}

int main()
{
   init();
    work();
    return 0; 
} 

LCT

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

const int M=200005;

int n,m,y;
int fa[M],c[M][2],st[M],sz[M],nxt[M];
bool rev[M];

inline bool f(int x){return c[fa[x]][1]==x;}
inline bool isr(int x){return c[fa[x]][0]!=x&&c[fa[x]][1]!=x;}
void pushup(int x){sz[x]=sz[c[x][0]]+sz[c[x][1]]+1;}

void pushdown(int x)
{
    if(!rev[x]) return ;
    int &l=c[x][0],&r=c[x][1];
    rev[l]^=1;rev[r]^=1;swap(l,r);
    rev[x]=0;
}

void rotate(int x)
{
    int p=fa[x],g=fa[p],r;
    bool k=f(x),m=f(p);r=c[x][k^1];
    if(!isr(p)) c[g][m]=x;
    fa[x]=g,c[x][k^1]=p;
    fa[p]=x,c[p][k]=r;
    if(r) fa[r]=p;
    pushup(p);
}

void maintain(int x)
{
    int top=0;st[++top]=x;
    for(int i=x;!isr(i);i=fa[i]) st[++top]=fa[i];
    for(int i=top;i;i--) pushdown(st[i]);
}

void splay(int x)
{
    maintain(x);
    for(;!isr(x);rotate(x))
        if(!isr(fa[x])) rotate(f(x)==f(fa[x])?fa[x]:x);
    pushup(x);
}

void access(int x){for(y=0;x;c[x][1]=y,y=x,x=fa[x]) splay(x);}//取出当前点到当前根的这一段路径,将它们放到一个Splay中。

void rever(int x){access(x);splay(x);rev[x]^=1;}//将x旋转到其所在树的树根 

void link(int x,int y){rever(x);fa[x]=y;splay(x);}//将x连到y的下面 

void cut(int x,int y){rever(x);access(y);splay(y);fa[x]=c[y][0]=0;}//断开x和y之间的边 

void work()
{
    int opt,u,v;
    while(m--)
    {
        scanf("%d%d",&opt,&u);u++;
        if(opt==1) rever(n+1),access(u),splay(u),printf("%d\n",sz[c[u][0]]);
        if(opt==2) scanf("%d",&v),cut(u,nxt[u]),nxt[u]=min(u+v,n+1),link(u,nxt[u]);
    }
}

void init()
{
    int u;
    cin>>n;
    for(int i=1;i<=n;i++){
        scanf("%d",&u);
        u=min(i+u,n+1);
        fa[i]=nxt[i]=u;sz[i]=1;
    }
    sz[n+1]=1;
    cin>>m;
}

int main()
{
    init();
    work();
    return 0;
} 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值