[uoj207]共价大爷游长沙——lct

题目大意:

给定一棵树和一些路径的集合,同时树和集合都可以支持在线修改,然后在某一时刻求一条边是否被所有集合之中的路径给覆盖。

思路:

考虑一个简单的思路,每当添加一条路径,我们就把在这条路径上的所有边的边权都异或上一个随机值,然后对于任意一条需要询问的边,我们只需要判断它的权值是否等于目前所有的路径的权值的异或和即可。
当我们的权值很大的时候,出错的概率很低,所以可以近似为正确的。
但是树的形态也需要动态修改,这就说明一条路径在不同的版本中,它所代表的边是不一样的,这就很麻烦。
但是仔细观察一下就可以发现,新加的边一定会在树上形成一个环,然后所有在环中经过那条需要删除的边的路径都会绕道,也就是在环上走原来的路径的补集,这样的话,我们只要将整个环异或上这个删除的边的边权,这样便正好达到了取补集的效果。
于是只需要用lct维护一下边权和即可。

/*=======================================
 * Author : ylsoi
 * Time : 2019.1.28
 * Problem : uoj207
 * E-mail : [email protected]
 * ====================================*/
#include<bits/stdc++.h>

#define REP(i,a,b) for(int i=a,i##_end_=b;i<=i##_end_;++i)
#define DREP(i,a,b) for(int i=a,i##_end_=b;i>=i##_end_;--i)
#define debug(x) cout<<#x<<"="<<x<<" "
#define pii pair<int,int>
#define fi first
#define se second
#define mk make_pair
#define pb push_back
typedef long long ll;

using namespace std;

void File(){
    freopen("uoj207.in","r",stdin);
    freopen("uoj207.out","w",stdout);
}

template<typename T>void read(T &_){
    _=0; T fl=1; char ch=getchar();
    for(;!isdigit(ch);ch=getchar())if(ch=='-')fl=-1;
    for(;isdigit(ch);ch=getchar())_=(_<<1)+(_<<3)+(ch^'0');
    _*=fl;
}

const int maxn=3e5+10;
const int mod=2e9;
int task_id,n,m;

struct Link_Cut_Tree{
#define rel(o) (ch[fa[o]][1]==o)
#define lc ch[o][0]
#define rc ch[o][1]
#define isrt(o) (ch[fa[o]][0]!=o && ch[fa[o]][1]!=o)
    int ch[maxn][2],fa[maxn],w[maxn],laz[maxn];
    int st[maxn];
    bool tag[maxn];
    void pushdown(int o){
        if(tag[o]){
            swap(lc,rc);
            tag[lc]^=1,tag[rc]^=1,tag[o]=0;
        }
        if(laz[o]){
            w[lc]^=laz[o],laz[lc]^=laz[o];
            w[rc]^=laz[o],laz[rc]^=laz[o];
            laz[o]=0;
        }
    }
    void rotate(int o){
        int f=fa[o],r=rel(o);
        fa[o]=fa[f]; if(!isrt(f))ch[fa[f]][rel(f)]=o;
        fa[ch[o][r^1]]=f; ch[f][r]=ch[o][r^1];
        fa[f]=o; ch[o][r^1]=f;
    }
    void splay(int o){
        int cnt=0,p=o;
        while(1){
            st[++cnt]=p;
            if(isrt(p))break;
            p=fa[p];
        }
        DREP(i,cnt,1)pushdown(st[i]);
        for(int f;!isrt(o);rotate(o)){
            f=fa[o];
            if(!isrt(f))rotate(rel(f)==rel(o) ? f : o);
        }
    }
    void access(int o){
        for(int res=0;o;){
            splay(o),rc=res;
            res=o,o=fa[o];
        }
    }
    void mkrt(int o){
        access(o),splay(o),tag[o]^=1;
    }
    void split(int x,int y){
        mkrt(x),access(y),splay(y);
    }
    void link(int x,int y){
        mkrt(x),fa[x]=y;
    }
    void cut(int x,int y){
        split(x,y);
        ch[y][0]=fa[x]=0;
    }
    void update(int x,int y,int z){
        split(x,y),w[y]^=z,laz[y]^=z;
    }
}T;

int cnte,sum,w[maxn],tot;
pii node[maxn];
set<pii>G[maxn];
set<pii>::iterator it;

int e(int x,int y){
    it=G[x].lower_bound(mk(y,0));
    return it->se;
}

int main(){
    File();
    srand((unsigned)time(NULL));
    read(task_id);
    read(n),read(m);

    cnte=n;
    int u,v;
    REP(i,2,n){
        ++cnte;
        read(u),read(v);
        G[u].insert(mk(v,cnte));
        G[v].insert(mk(u,cnte));
        T.link(u,cnte);
        T.link(v,cnte);
    }

    int ty,x,y;
    REP(i,1,m){
        read(ty);
        if(ty==1){
            read(x),read(y),read(u),read(v);
            int id=e(x,y);
            T.splay(id);
            int t=T.w[id];
            T.w[id]=0;
            T.cut(x,id),G[x].erase(mk(y,id));
            T.cut(id,y),G[y].erase(mk(x,id));
            T.link(u,id),G[u].insert(mk(v,id));
            T.link(v,id),G[v].insert(mk(u,id));
            T.update(x,y,t);
        }
        else if(ty==2){
            read(x),read(y);
            ++tot;
            w[tot]=1ll*rand()*rand()%mod;
            node[tot]=mk(x,y);
            sum^=w[tot];
            T.update(x,y,w[tot]);
        }
        else if(ty==3){
            read(x);
            sum^=w[x];
            T.update(node[x].fi,node[x].se,w[x]);
        }
        else if(ty==4){
            read(x),read(y);
            int id=e(x,y);
            T.splay(id);
            printf("%s\n",T.w[id]==sum ? "YES" : "NO");
        }
    }

    return 0;
}

原文地址:https://www.cnblogs.com/ylsoi/p/10331394.html

时间: 2024-08-28 21:50:56

[uoj207]共价大爷游长沙——lct的相关文章

【LCT维护子树信息】uoj207 共价大爷游长沙

这道题思路方面就不多讲了,主要是通过这题学一下lct维护子树信息. lct某节点u的子树信息由其重链的一棵splay上信息和若干轻儿子子树信息合并而成. splay是有子树结构的,可以在rotate,access的时候由儿子update到父亲,而轻儿子的信息update不上来,需要另外记一下. 记sum[x]为我们要求的子树信息,xu[x]为x的轻儿子的子树信息. (即,xu[x]由轻儿子的sum更新,sum[x]由xu[x]和splay子树上的儿子的sum更新. 这样我们就可以完整地用lct维

[UOJ207]共价大爷游长沙

如果每次加入点对$(x,y)$,就给它一个随机的权值$v$,把两个点的点权都异或$v$,查询$(x,y)$的时候,只要把$x$硬点为根,以$y$为根的子树的异或和等于当前所有的异或和,那么很大概率就是正确的(每对点刚好有一个在$y$的子树中) 所以直接用lct维护即可,因为维护了虚边信息,所以link时两边都要makeroot(不然没法更新splay里的祖先的信息),cut时不改虚边信息 splay之前一定要记得一路pushdown啊啊啊啊啊啊! #include<stdio.h> #incl

UOJ #207. 共价大爷游长沙

#207. 共价大爷游长沙 链接:http://uoj.ac/problem/207 题意:给一棵树,要求支持加边.删边.询问一条边是否被所有路径覆盖.同时路径端点集合有加入与删除操作. 想法: 考虑一个点与其父亲边是否被一条路径经过.就是该路径的一端在其子树中,另一端不在.就是其子树中一条路径的端点出现次数为奇数.随机给一条路径两端一个权值(错误概率为$\frac{n^2}{2^w}$),然后如果一个节点子树xor值等于当前路径xor值,其到父亲边就是可行的边. 然后便是LCT维护加边,删边,

@uoj - [email&#160;protected] 共价大爷游长沙

目录 @[email protected] @[email protected] @accepted [email protected] @[email protected] @[email protected] 火车司机出秦川,跳蚤国王下江南,共价大爷游长沙.每个周末,勤劳的共价大爷都会开车游历长沙市. 长沙市的交通线路可以抽象成为一个 n 个点 n?1 条边的无向图,点编号为 1 到 n,任意两点间均存在恰好一条路径,显然两个点之间最多也只会有一条边相连.有一个包含一些点对 (x,y) 的可

【uoj207】 共价大爷游长沙

http://uoj.ac/problem/207 (题目链接) 题意 给出一棵无根树,4种操作:在路径集合中加入一条路径,在路径集合中删除一条路径,删一条边加一条边,查询一条边是否被集合中所有路径经过. Solution 将路径端点同时异或上一个值,那么如果一条路径被经过,那么它的子树中点的异或和一定等于所有路径的异或和. 考虑如何用LCT维护这种可加减的子树信息. 对于询问,我们将询问的点access一下,那么它的所有虚儿子就是它在真实的树中的所有儿子了. 对于会使轻重边切换的操作:acce

UOJ207:共价大爷游长沙

题面 UOJ Sol 神题 给每个点对随机一个权值,把这两个点的权值异或上这个随机的值 用\(LCT\)维护子树信息,若子树异或和为所有点对的异或和那么就是答案 大常数代码 # include <bits/stdc++.h> # define RG register # define IL inline # define Fill(a, b) memset(a, b, sizeof(a)) using namespace std; typedef long long ll; const int

#207. 共价大爷游长沙

题目描述 http://uoj.ac/problem/207 题解 因为这道题有删边和加边的操作,所以我们不能再链上操作,只能在点上操作. 考虑一些正确性玄学的算法. 我们给每一次链加随机一个权值,这样对于每次询问就查一下这条边分成的两块中的权值异或和是否等于当前所有链的权值异或和即可. 代码 #include<iostream> #include<cstdio> #include<cstdlib> #define ls ch[x][0] #define rs ch[x

总结与心得(持续更新)

不知道为什么,刚学的算法过了2个月就忘得一干二净,我并没有背代码啊,当时学的时候还刷了好多题来着→_→,我是不是大脑能力严重衰退了. 动态规划 单调队列 一般情况下,${dp}$方程可以搞成这样:${f_i=f_j+t_j+t_i}$,只要其中没有变量同时与${i,j}$都有关,那么我们可以用单调队列来做,单调队列里面元素的关键字就是与${j}$有关的东西${f_j+t_j}$.example:生产产品 有些比较特殊的,虽然存在同时与${i,j}$相关的函数,但是这个函数比较简单,使得已经存在在

成都七中

Day1T1感觉是一个DP貌似还是一个不太会的数位DP暴力...T2暴力然而刚刚学还是不太会还是暴力,感觉能拿点分 下午是LCT不过在之前讲了一大堆的势能分析然后O(1)秒掉了BZOJ上的一道题感觉还是挺6的然后就是LCT板子还不是太熟大概只能听一下处理LCT问题的思想 晚上打了BZOJ上的大融合本来还想过了共价大爷游长沙不过splay的标记不会打就看了标程,看懂了差不多也回宿舍了.. Day2T1就是暴力搞一搞加一个剪枝就A了然而我并没有减掉T2 扔了一个贪心上去T3 n^2暴力-->40 下