1. 程式人生 > >題解 洛谷P3203/BZOJ2002【[HNOI2010]彈飛綿羊】

題解 洛谷P3203/BZOJ2002【[HNOI2010]彈飛綿羊】

裸的LCT,關鍵是要怎麼連邊,怎麼將這種彈飛關係轉化成連邊就行了。

那麼我們可以這樣連邊:

  • 一個節點i的爸爸就是i+ki。

  • 沒有i+ki那麼就被彈飛了,即$i$的爸爸是虛擬節點n+1。

那麼怎麼求次數呢?

  • i的深度就是次數

對於求深度,我們可以先將x弄成root,然後通過Access(n+1)n+1號節點和x節點丟到一個Splay裡面,維護一個size,每次詢問的answer就是已經Splay到根的n+1號節點的size了。

Code:

#include<bits/stdc++.h>
#define ll long long
#define inf 0x3f3f3f3f
#define RI register int
using namespace std;
const int N=2e5+2;
template <typename Tp> inline void IN(Tp &x){
    int f=1;x=0;char ch=getchar();
    while(ch<'0'||ch>'9')if(ch=='-')f=-1,ch=getchar();
    while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();x*=f;
}int f[N],s[N],r[N],val[N],hep[N],ch[N][2];
inline int get(int x){return ch[f[x]][0]==x||ch[f[x]][1]==x;}
inline void pushup(int x){s[x]=s[ch[x][0]]+s[ch[x][1]]+1;} 
inline void pushdown(int x){
    if(!r[x])return;r[x]=0;
    swap(ch[x][0],ch[x][1]);
    if(ch[x][0])r[ch[x][0]]^=1;
    if(ch[x][1])r[ch[x][1]]^=1;
}
inline void rotate(int x){
    int y=f[x],z=f[y],k=ch[y][1]==x,v=ch[x][!k];
    if(get(y))ch[z][ch[z][1]==y]=x;ch[x][!k]=y,ch[y][k]=v;
    if(v)f[v]=y;f[y]=x,f[x]=z;pushup(y),pushup(x); 
} 
inline void Splay(int x){
    int y=x,top=0;hep[++top]=y;
    while(get(y))hep[++top]=y=f[y];
    while(top)pushdown(hep[top--]);
    while(get(x)){
        y=f[x],top=f[y];
        if(get(y))
          rotate((ch[y][0]==x)^(ch[top][0]==y)?x:y);
        rotate(x);
    }pushup(x);return;
}
inline void Access(int x){
    for(register int y=0;x;x=f[y=x])
       Splay(x),ch[x][1]=y,pushup(x);
}
inline int findroot(int x){
    Access(x);Splay(x);
    while(ch[x][0])pushdown(x),x=ch[x][0];
    return x;
}
inline void makeroot(int x){Access(x);Splay(x);r[x]^=1;}
inline void split(int x,int y){makeroot(x);Access(y);Splay(y);} 
inline void link(int x,int y){makeroot(x);if(findroot(y)!=x)f[x]=y;}
inline void cut(int x,int y){
    split(x,y);if(findroot(y)==x&&f[x]==y&&!ch[x][1])
    {f[x]=ch[y][0]=0;pushup(y);}return;
}int n,m;
int main(){
    scanf("%d",&n);
    for(register int i=1;i<=n+1;++i)s[i]=1;
    for(register int x,i=1;i<=n;++i){
        scanf("%d",&x);val[i]=x;
        link(i,(i+x<=n)?i+x:n+1);
    }scanf("%d",&m);
    for(register int op,x,y,i=1;i<=m;++i){
        scanf("%d%d",&op,&x);++x;
        if(op==1){
            makeroot(x);Access(n+1);Splay(n+1);
            printf("%d\n",s[n+1]-1);
        }else if(op==2){
            scanf("%d\n",&y);
            cut(x,(x+val[x]<=n)?x+val[x]:n+1);
            link(x,(x+y<=n)?x+y:n+1);val[x]=y;
        }
    }return 0;
}

所以就沒了。