1. 程式人生 > >【LCT維護基環內向樹森林】BZOJ4764 彈飛大爺

【LCT維護基環內向樹森林】BZOJ4764 彈飛大爺

read 3.1 wap max limit spa register ostream turn

4764: 彈飛大爺

Time Limit: 30 Sec Memory Limit: 256 MB
Submit: 101 Solved: 52
[Submit][Status][Discuss]

Description

自從WC退役以來,大爺是越來越懶惰了。為了幫助他活動筋骨,也是受到了彈飛綿羊一題的啟發,機房的小夥伴們 決定齊心合力構造一個下面這樣的序列。這個序列共有N項,每項都代表了一個小夥伴的力量值,如果大爺落到了 第i個小夥伴的手裏,那麽第i個小夥伴會把大爺彈到第i+ai個小夥伴手裏,其中ai就是第i個小夥伴的力量值,也 就是序列的第i項。然而,因為大爺太沈了,所以有些小夥伴不能撐到鍛(you)煉(xi)結束,所以我們中途會替 換一些小夥伴,也就是改變序列的某些項。而且,因為大爺太沈了,所以有些小夥伴不能把大爺扔向前方,而是會 把大爺往反方向扔,也就是序列中的一些項會是負的(當然,也可能是零嘍)。現在機智的大爺通過在空中的觀察 ,已經知道小夥伴們的所有活動——即初始序列、所有更改操作,他想請你算一算,如果他在某時刻落到了某個位 置,那麽他會在幾次彈起之後落到小夥伴序列之外(畢竟摔在地上還是蠻疼的)。

Input

第一行為兩個整數N和M,代表序列長度和操作次數。 第二行為N個整數,代表初始的小夥伴序列。 接下來有M行,每行代表一個操作。 如果這一行的第一個數是1,代表該操作是一個詢問操作,接下來一個數X,代表詢問此時大爺從X處,經過幾次彈 起會摔在地上。如果永遠不會摔在地上,請輸出-1。 如果這一行的第一個數是2,代表該操作是一個更改操作,接下來兩個數X,Y,代表將序列的第X項改為Y。 N,M <= 200000 |Ai| < N

Output

對於每次詢問操作,輸出彈起次數或-1。

Sample Input

3 19
1 1 1
1 1
1 2
1 3
2 1 2
1 1
1 2
1 3
2 3 -1
1 1
1 2
1 3
2 2 233
1 1
1 2
1 3
2 2 -233
1 1
1 2
1 3

Sample Output



3
2
1
2
2
1
-1
-1
-1
3
1
2
3
1
2

題解

LCT維護基環內向樹森林

具體來說,就是當我們原來維護的無向圖變成了一個每個點出度為1的有向圖

顯然它是可能存在環的,並且更重要的是它不能換根(方向問題)

所以若它是一棵樹的話,正常維護LCT

如果它形成了環,我們記一下這個聯通塊的根到達的點的位置pos[i]

對於Link操作,我們判斷一下這條有向邊x--->y的端點y在被連接之前的根節點是否已經可以是x

  • 如果是,那麽加上這條邊之後形成環,我們顯然不能把它加上,所以我們令pos[x]=y
  • 如果不是,正常地把x連到y的下面

對於Cut操作,我們要判斷一下這條有向邊x--->y的端點x形成的環是否正好連到y,即判斷pos[x]是否等於y

  • 如果是,那麽直接把環去掉,即令pos[x]=0
  • 如果不是,我們先記一下x在聯通塊中的根節點rt,然後正常地去掉這條邊
  • 但是然後我們還要判斷一下割斷這條邊後是否會保留原來的環,如果已經沒環,就把記錄的邊加上,如果環還在,退出

具體看代碼吧

代碼

//by 減維
#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#include<bitset>
#include<set>
#include<cmath>
#include<vector>
#include<set>
#include<map>
#include<ctime>
#include<algorithm>
#define ll long long
#define il inline
#define rg register
#define db double
#define mpr make_pair
#define maxn 200005
#define inf (1<<30)
#define eps 1e-8
#define pi 3.1415926535897932384626L
using namespace std;

inline int read()
{
    int ret=0;bool fla=0;char ch=getchar();
    while((ch<0||ch>9)&&ch!=-)ch=getchar();
    if(ch==-){fla=1;ch=getchar();}
    while(ch>=0&&ch<=9){ret=ret*10+ch-0;ch=getchar();}
    return fla?-ret:ret;
}

int n,m,fa[maxn],son[maxn][2],siz[maxn],rev[maxn],pos[maxn];
int a[maxn];

il bool pdp(int x){return son[fa[x]][1]==x;}
il bool isrt(int x){return son[fa[x]][0]!=x&&son[fa[x]][1]!=x;}
il void rever(int x){rev[x]^=1;swap(son[x][0],son[x][1]);}
il void upda(int x){siz[x]=siz[son[x][0]]+siz[son[x][1]]+1;}

il void pdn(int x)
{
    if(rev[x])
    {
        if(son[x][0]) rever(son[x][0]);
        if(son[x][1]) rever(son[x][1]);
        rev[x]=0;
    }
}

void pd(int x){if(!isrt(x)) pd(fa[x]);pdn(x);}

il void rot(int x)
{
    int f=fa[x],g=fa[f],o=pdp(x);
    if(!isrt(f)) son[g][pdp(f)]=x;fa[x]=g;
    son[f][o]=son[x][!o];fa[son[f][o]]=f;
    son[x][!o]=f;fa[f]=x;
    upda(f),upda(x);
}

il void splay(int x)
{
    pd(x);
    for(;!isrt(x);rot(x))
        if(!isrt(fa[x])) rot(pdp(fa[x])==pdp(x)?fa[x]:x);
}

il void acc(int x)
{
    for(int y=0;x;y=x,x=fa[x])
        splay(x),son[x][1]=y,upda(x);
}

il int find(int x)
{
    acc(x),splay(x);
    while(son[x][0]) pdn(x),x=son[x][0];
    return x;
}

il void link(int x,int y)
{
    if(find(y)==x) pos[x]=y;
    else acc(x),splay(x),fa[x]=y;
}

il void cut(int x,int y)
{
    if(pos[x]==y) pos[x]=0;
    else{
        int t=find(x);
        acc(x),splay(x),fa[son[x][0]]=0,son[x][0]=0,upda(x);
        if(pos[t]&&find(pos[t])!=t) link(t,pos[t]),pos[t]=0;
    }
}

int main()
{
    n=read(),m=read();
    for(int i=1;i<=n;++i) a[i]=read(),siz[i]=1;
    for(int i=1;i<=n;++i)
        if(i+a[i]>=1&&i+a[i]<=n) link(i,a[i]+i);
    for(int i=1,op,x,y;i<=m;++i)
    {
        op=read(),x=read();
        if(op==1){
            acc(x),y=find(x);
            if(pos[y]) puts("-1");
            else splay(x),printf("%d\n",siz[x]);
        }else{
            y=read();
            if(x+a[x]>=1&&a[x]+x<=n) cut(x,a[x]+x);
            if(x+y>=1&&x+y<=n) link(x,x+y);
            a[x]=y;
        }
    }
    return 0;
}

【LCT維護基環內向樹森林】BZOJ4764 彈飛大爺