hdu5967 小R与手机

链接

http://acm.hdu.edu.cn/showproblem.php?pid=5967

题解

用LCT,不要 m a k e r o o t makeroot makeroot
一棵树的根节点可能是真的根节点 ( a [ r o o t ] = 0 ) (a[root]=0) (a[root]=0),也可能这个根节点还指向树里的另一个节点(这种情况下 a [ r o o t ] = ̸ 0 a[root]=\not0 a[root]≠0)
更改指向的时候,先切断原来的边,切断边之后,可能会破坏一个环,这时就要让假的根节点指向它应该指向的节点
切完了之后,再连接
查询祖先的时候,先找到这颗树的根节点,然后看下根节点是不是真的根节点 ( a [ r o o t ] = 0 ) (a[root]=0) (a[root]=0),如果是就输出其编号;但如果是假的根节点 ( a [ r o o t ] = ̸ 0 ) (a[root]=\not 0) (a[root]≠0),就输出 − 1 -1 1

代码

#include <bits/stdc++.h>
#define maxn 200010
using namespace std;
struct LinkCutTree
{
    int rev[maxn], f[maxn], ch[maxn][2], s[maxn];
    int getwh(int x)
    {if(f[x]==0)return -1;if(ch[f[x]][0]==x)return 0;if(ch[f[x]][1]==x)return 1;return -1;}
    bool isroot(int x){return getwh(x)==-1;}
    void maketag_rev(int x){if(x)rev[x]^=1;}
    void join(int x, int y, int wh){if(x)f[x]=y;if(y)ch[y][wh]=x;}
    void pushdown(int x)
    {
        if(rev[x])
        {
            swap(ch[x][0],ch[x][1]);
            maketag_rev(ch[x][0]), maketag_rev(ch[x][1]);
            rev[x]=0;
        }
    }
    void rotate(int x)
    {
        int y=f[x], z=f[y];
        int c=getwh(x);
        if(!isroot(y))join(x,z,getwh(y));
        else f[x]=f[y];
        join(ch[x][!c],y,c);
        join(y,x,!c);
    }
    void splay(int x)
    {
        int y, top(0);
        for(y=x;!isroot(y);y=f[y])s[++top]=y;s[++top]=y;
        for(;top;top--)pushdown(s[top]);
        while(!isroot(x))
        {
            y=f[x];
            if(isroot(y)){rotate(x);break;}
            if(getwh(x)^getwh(y))rotate(x);
            else rotate(y);
            rotate(x);
        }
    }
    void access(int x)
    {
        int t=0;
        while(x)
        {
            splay(x);
            ch[x][1]=t;
            t=x;x=f[x];
        }
    }
    void makeroot(int x){access(x);splay(x);maketag_rev(x);}
    void link(int x, int y){makeroot(x);f[x]=y;}
    void cut(int x, int y)
    {
        makeroot(x); access(y);
        splay(y);
        if(ch[y][0]==x and ch[x][1]==0)f[x]=ch[y][0]=0;
    }
    int findroot(int x)
    {
        access(x); splay(x);
        while(ch[x][0])pushdown(x), x=ch[x][0];
        splay(x);
        return x;
    }
}lct;
int read(int x=0)
{
    int c, f(1);
    for(c=getchar();!isdigit(c);c=getchar())if(c=='-')f=-f;
    for(;isdigit(c);c=getchar())x=x*10+c-0x30;
    return f*x;
}
int a[maxn], n, m;
int main()
{
    int i, j;
    n=read(), m=read();
    for(i=1;i<=n;i++)
    {
        a[i]=read();
        if(a[i]==0)continue;
        if(lct.findroot(a[i])!=i)
        {
            lct.splay(i);
            lct.f[i]=a[i];
        }
    }
    while(m--)
    {
        int type(read());
        if(type==1)
        {
            int x(read()), y(read());
            auto rt=lct.findroot(x);
            if(rt!=x)
            {
                lct.access(a[x]);
                lct.f[x]=0;
                if(lct.findroot(a[rt])!=rt)
                {
                    lct.splay(rt);
                    lct.f[rt]=a[rt];
                }
            }
            if(y and lct.findroot(y)!=x)
            {
                lct.splay(x);
                lct.f[x]=y;
            }
            a[x]=y;
        }
        else
        {
            int x(read());
            int num=lct.findroot(x);
            if(a[num]==0)printf("%d\n",num);
            else printf("-1\n");
        }
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值