【题解】UOJ207 共价大爷游长沙

版权声明:写得不好,随便转载,但请注明出处,感激不尽 https://blog.csdn.net/qq_40515553/article/details/80580503

Problem

uoj

题目概要:
给定一棵树,维护四种操作:
1:断开一条边,加入一条边,保证操作后仍是树
2:往集合S中加入一个点对(x,y)
3:删除集合中一个点对
4:询问一条边,是否集合中所有点对之间的路径都要经过这条边

Solution

不是很难的一题……

想到某校自主招生题:给定序列,其中有一个数字出现一次,其余数字均出现两次,找出这个数(不用任何附加空间,线性解决)

运用异或成双抵消的性质,将整个序列异或一遍,最终整个序列异或和就是要找的那个数

这题同样可以运用上面的那个性质,只要一个点对分列于一条边的两端,则两端的异或和即为零,再根据异或的交换啥的,可得在有多组点对的情况下依然成立,即一个子树异或和为等于全集

最后可以用lct维护动态加边删边,而这题与其他题目不一样,需要维护子树信息,只要维护虚儿子的和即可,具体维护子树信息的lct可以做一道模板题:[BJOI2014]大融合

而为了保证准确性,不同的点对之间应该不能互相代替,而最优的一种权值集合就是:1,2,4,8,16…

而这题点对不一定在63以内,所以可以采取随机赋值的形式,准确率很高,只要出题人不恶心……(UOJ上的附加测试点就卡了直接rand(),但实际上只要换一种rand方式即可,我用的是(rand()*10000+rand())%1000000007)

整理下做法:

  • 给每一个点对附上相同的随机值
  • 询问即为询问被边分割的两棵树的权值异或和是否为零
  • 用lct存储虚儿子信息维护子树信息

Code

(如果对做法或lct维护子树信息不是很了解的话,代码可能要调很久)

//uoj-207
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define rg register

template <typename _Tp> inline _Tp read(_Tp&x){
    char c11=getchar(),ob=0;x=0;
    while(c11^'-'&&!isdigit(c11))c11=getchar();if(c11=='-')c11=getchar(),ob=1;
    while(isdigit(c11))x=x*10+c11-'0',c11=getchar();if(ob)x=-x;return x;
}

const int N=101000;
int n,m,S,tot;

struct ques{
    int l,r,w;
    ques(){}
    ques(const int&A,const int&B,const int&C){l=A,r=B,w=C;}
}qu[3*N];

    int ch[N][2],ksz[N],sz[N],rev[N],q[N],fa[N],top;
    #define L ch[x][0]
    #define R ch[x][1]
    inline int blood(int x){return ch[fa[x]][1]==x;}
    inline int isroot(int x){return ch[fa[x]][0]!=x&&ch[fa[x]][1]!=x;}
    inline void up(int x){sz[x]=sz[L]^sz[R]^ksz[x];}
    inline void down(int x){if(rev[x])rev[L]^=1,rev[R]^=1,rev[x]^=1,swap(L,R);return ;}
    inline void rotate(int x){
        int f=fa[x],grand=fa[f],l=blood(x),r=l^1;
        if(!isroot(f))ch[grand][ch[grand][1]==f]=x;
        fa[x]=grand,fa[f]=x,fa[ch[x][r]]=f;
        ch[f][l]=ch[x][r],ch[x][r]=f;
        up(f);up(x);return ;
    }
    inline void splay(int x){
        q[top=1]=x;
        for(rg int i=x;!isroot(i);i=fa[i])q[++top]=fa[i];
        for(rg int i=top;i;--i)down(q[i]);
        for(;!isroot(x);rotate(x))if(!isroot(fa[x]))rotate(blood(fa[x])==blood(x)?fa[x]:x);
        return ;
    }
    inline void access(int x){for(int t=0;x;t=x,x=fa[x])splay(x),ksz[x]^=sz[R]^sz[t],R=t,up(x);}
    inline void makeroot(int x){access(x);splay(x);rev[x]^=1;}
    inline void split(int x,int y){makeroot(x);access(y);splay(y);return ;}
    inline void link(int x,int y){makeroot(x);makeroot(y);fa[x]=y;ksz[y]^=sz[x];up(y);}
    inline void cut(int x,int y){split(x,y);if(ch[y][0]==x)fa[x]=ch[y][0]=0;up(y);return ;}
    #undef L
    #undef R

inline void update(int x,int Val){access(x);splay(x);ksz[x]^=Val;sz[x]^=Val;}

int main(){
    read(n);read(n);read(m);
    int x,y;
    for(rg int i=1;i<n;++i){read(x);read(y);link(x,y);}
    int opt,z;
    while(m--){
        read(opt);
        switch(opt){
            case 1:
                read(x);read(y);cut(x,y);
                read(x);read(y);link(x,y);
                break;
            case 2:
                read(x);read(y);S^=(z=(rand()*10000+rand())%1000000007);
                qu[++tot]=ques(x,y,z);
                update(x,z),update(y,z);
                break;
            case 3:
                read(x);S^=qu[x].w;
                update(qu[x].l,qu[x].w);
                update(qu[x].r,qu[x].w);
                break;
            case 4:
                read(x);read(y);split(x,y);
                if(ksz[y]==S)puts("YES");
                else puts("NO");
                break;
            default : puts("ERROR");return 0;
        }
    }return 0;
}
阅读更多
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页