UOJ 207 共价大爷游长沙

LCT

看见树边可以删除和加入,就会直接想到用LCT,但是如何用LCT维护如题所述的边的信息呢?

考虑一个询问(u,v),如果他是合法的,那么所有(共价大爷走的)链都一定经过它。换一个角度,也就是说所有链的两个端点都一定分居这条边两侧!这提醒可以我们从端点入手,而不是边。

如何判断所有所有链的两个端点是否分居两侧?用异或值!我们对于每一个加入的链,在链的两端点异或上一个随机大整数。对于询问(u,v),假设v的深度较大,如果v的子树的异或值能够等于所有链的总异或值,那么我们就可以认为它被所有链穿过了。

于是我们只要用LCT维护子树即可,具体就是维护两个值val和sum,详见代码。

#include<cstdio>
#include<algorithm>
#define ll long long
#define N 100005
#define M 300005
using namespace std;
int lcnt, n, m;
struct Line
{
    int x, y;
    ll v;
}line[M];
struct node *null;
struct node
{
    node *fa, *ch[2];
    ll val, sum;
    //val = 自己的值 + 所有虚儿子的sum 
    //sum = 自己的值 + 所有实儿子的sum + 所有虚儿子的sum 
    bool rev;
    void modify(ll v)
    {
        val ^= v;
        sum ^= v;
    }
    void modify_v(ll v)
    {
        access();
        splay();
        val ^= v;
        sum ^= v;
    }
    void push_up()
    {
        sum = val ^ ch[0]->sum ^ ch[1]->sum;
    }
    void push_down()
    {
        if(!rev)return;
        if(ch[0] != null)ch[0]->do_rev();
        if(ch[1] != null)ch[1]->do_rev();
        rev = 0;
    }
    int type()
    {
        return fa->ch[0] == this ? 0 : 1;
    }
    bool isroot()
    {
        return fa->ch[type()] != this;
    }
    void do_rev()
    {
        rev^=1;
        node *tmp = ch[0];
        ch[0] = ch[1];
        ch[1] = tmp;
    }
    void rotate()
    {
        node *gfa = fa->fa, *ffa = fa;
        int f1 = type(), f2 = fa->type();
        if(!fa->isroot())
            gfa->ch[f2] = this;
        node *c = ch[f1^1];
        if(c != null)c->fa=ffa;
        this->fa=gfa;
        this->ch[f1^1]=ffa;
        ffa->fa=this;
        ffa->ch[f1]=c;
        ffa->push_up();
    }
    void pre()
    {
        if(!isroot())fa->pre();
        push_down();
    }
    void splay()
    {
        pre();
        while(!isroot())
        {
            if(!fa->isroot())
                type() == fa->type() ? fa->rotate() : rotate();
            rotate();
        }
        push_up();
    }
    void access()
    {
        node *tmp = null, *p = this;
        for(; p!=null; tmp = p, p = p->fa)
        {
            p->splay();
            p->modify(p->ch[1]->sum ^ tmp->sum);
            p->ch[1] = tmp;
            p->push_up();
        }
    }
    void make_root()
    {
        access();
        splay();
        do_rev();
    }
    void link(node *p)
    {
        make_root();
        fa = p;
        p->modify_v(sum);
    }
    void cut(node *p)
    {
        make_root();
        p->access();
        p->splay();
        p->ch[0] = fa = null;
        p->push_up();
    }
}t[N];
void link(int a, int b){t[a].link(&t[b]);}
void cut(int a, int b){t[a].cut(&t[b]);}
void init()
{
    null = new node;
    null -> fa = null;
    null -> ch[0] = null -> ch[1] =null;
    node normal = (node){null,null,null,0,0};
    for(int i = 1; i <= n; i++)
        t[i] = normal;
}
ll Random()
{
    return 1ll*rand()*rand()*rand()*rand();
}
int main()
{
    ll all = 0;
    scanf("%d",&n);
    scanf("%d%d",&n,&m);
    init();
    for(int i = 1, a, b; i < n; i++)
    {
        scanf("%d%d",&a,&b);
        link(a,b);
    }
    while(m--)
    {
        int opt;
        scanf("%d",&opt);
        if(opt == 1)
        {
            int x, y, u, v;
            scanf("%d%d%d%d",&x,&y,&u,&v);
            cut(x,y);
            link(u,v);
        }
        else if(opt == 2)
        {
            int x, y;
            ll v;
            scanf("%d%d",&x,&y);
            line[++lcnt]=(Line){x,y,v = Random()};
            all ^= v;
            t[x].modify_v(v);
            t[y].modify_v(v);
        }
        else if(opt == 3)
        {
            int timer;
            scanf("%d",&timer);
            all ^= line[timer].v;
            t[line[timer].x].modify_v(line[timer].v);
            t[line[timer].y].modify_v(line[timer].v);
        }
        else
        {
            int x, y;
            scanf("%d%d",&x,&y);
            t[x].make_root();
            t[y].access();
            t[x].splay();
            if(t[y].sum == all)
                puts("YES");
            else puts("NO");
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值