• BZOJ4764弹飞大爷——LCT


    题目描述

    自从WC退役以来,大爷是越来越懒惰了。为了帮助他活动筋骨,也是受到了弹飞绵羊一题的启发,机房的小伙伴们
    决定齐心合力构造一个下面这样的序列。这个序列共有N项,每项都代表了一个小伙伴的力量值,如果大爷落到了
    第i个小伙伴的手里,那么第i个小伙伴会把大爷弹到第i+ai个小伙伴手里,其中ai就是第i个小伙伴的力量值,也
    就是序列的第i项。然而,因为大爷太沉了,所以有些小伙伴不能撑到锻(you)炼(xi)结束,所以我们中途会替
    换一些小伙伴,也就是改变序列的某些项。而且,因为大爷太沉了,所以有些小伙伴不能把大爷扔向前方,而是会
    把大爷往反方向扔,也就是序列中的一些项会是负的(当然,也可能是零喽)。现在机智的大爷通过在空中的观察
    ,已经知道小伙伴们的所有活动——即初始序列、所有更改操作,他想请你算一算,如果他在某时刻落到了某个位
    置,那么他会在几次弹起之后落到小伙伴序列之外(毕竟摔在地上还是蛮疼的)。

    输入

    第一行为两个整数N和M,代表序列长度和操作次数。
    第二行为N个整数,代表初始的小伙伴序列。
    接下来有M行,每行代表一个操作。
    如果这一行的第一个数是1,代表该操作是一个询问操作,接下来一个数X,代表询问此时大爷从X处,经过几次弹
    起会摔在地上。如果永远不会摔在地上,请输出-1。
    如果这一行的第一个数是2,代表该操作是一个更改操作,接下来两个数X,Y,代表将序列的第X项改为Y。
    N,M <= 200000  |Ai| < N

    输出

    对于每次询问操作,输出弹起次数或-1。

    样例输入

    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

    样例输出

    3
    2
    1
    2
    2
    1
    -1
    -1
    -1
    3
    1
    2
    3
    1
    2
     
    BZOJ2002的进阶版。
    可以发现每个联通块要么是一棵树,要么是一棵内向基环树。
    树的话直接像弹飞绵羊那样用LCT维护就行了。
    对于基环树我们假设多出的那条边是树根指向另一个点,那么对于这条边我们不连接它,而是存起来。
    在删除一条边时如果是基环上的边,那么就可以将之前树根存的那条边连上了。
    判断能否弹出直接看当前点是在树中还是基环树中即可。
    注意因为这是有向边,所以不能随意改变原树的根。
    #include<set>
    #include<map>
    #include<queue>
    #include<stack>
    #include<cmath>
    #include<vector>
    #include<bitset>
    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    #define pr pair<int,int>
    #define ll long long
    using namespace std;
    int n,m;
    int opt;
    int x,y;
    int v[200010];
    int s[200010][2];
    int st[200010];
    int f[200010];
    int size[200010];
    int a[200010];
    int is_root(int rt)
    {
        return rt!=s[f[rt]][0]&&rt!=s[f[rt]][1];
    }
    int get(int rt)
    {
        return rt==s[f[rt]][1];
    }
    void pushup(int rt)
    {
        size[rt]=size[s[rt][0]]+size[s[rt][1]]+1;
    }
    void rotate(int rt)
    {
        int fa=f[rt];
        int anc=f[fa];
        int k=get(rt);
        if(!is_root(fa))
        {
            s[anc][get(fa)]=rt;
        }
        s[fa][k]=s[rt][k^1];
        f[s[fa][k]]=fa;
        s[rt][k^1]=fa;
        f[fa]=rt;
        f[rt]=anc;
        pushup(fa);
        pushup(rt);
    }
    void splay(int rt)
    {
        for(int fa;!is_root(rt);rotate(rt))
        {
            if(!is_root(fa=f[rt]))
            {
                rotate(get(rt)==get(fa)?fa:rt);
            }
        }
    }
    void access(int rt)
    {
        for(int x=0;rt;x=rt,rt=f[rt])
        {
            splay(rt);
            s[rt][1]=x;
            pushup(rt);
        }
    }
    int find(int rt)
    {
        access(rt);
        splay(rt);
        while(s[rt][0])
        {
            rt=s[rt][0];
        }
        splay(rt);
        return rt;
    }
    void link(int x,int y)
    {
        if(find(y)==x)
        {
            v[x]=y;
        }
        else
        {
            access(x);
            splay(x);
            f[x]=y;
        }
    }
    void cut(int x,int y)
    {
        if(v[x]==y)
        {
            v[x]=0;
        }
        else
        {
            int root=find(x);
            access(x);
            splay(x);
            f[s[x][0]]=0;
            s[x][0]=0;
            pushup(x);
            if(v[root]&&find(v[root])!=root)
            {
                link(root,v[root]);
                v[root]=0;
            }
        }
    }
    int main()
    {
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&a[i]);
            size[i]=1;
            if(i+a[i]<=n&&i+a[i]>=1)
            {
                link(i,i+a[i]);
            }
        }
        while(m--)
        {
            scanf("%d",&opt);
            if(opt==1)
            {
                scanf("%d",&x);
                int root=find(x);
                if(v[root])
                {
                    printf("-1
    ");
                }
                else
                {
                    access(x);
                    splay(x);
                    printf("%d
    ",size[x]);
                }
            }
            else
            {
                scanf("%d%d",&x,&y);
                if(x+a[x]<=n&&x+a[x]>=1)
                {
                    cut(x,x+a[x]);
                }
                a[x]=y;
                if(x+a[x]<=n&&x+a[x]>=1)
                {
                    link(x,x+a[x]);
                }
            }
        }
    }
  • 相关阅读:
    考研机试 5.反序输出
    考研机试 4.代理服务器
    考研机试 3.约数的个数
    考研机试 8.整数拆分
    考研机试 6.手机键盘
    考研机试 2.成绩排序
    监督学习与非监督学习的区别
    关于调整input里面的输入光标大小
    JS读取cookie(记住账号密码)
    html嵌套规则
  • 原文地址:https://www.cnblogs.com/Khada-Jhin/p/9954707.html
Copyright © 2020-2023  润新知