BZOJ - 3282 Tree(动态树模板)

3282: Tree

Time Limit: 30 Sec   Memory Limit: 512 MB
Submit: 2500   Solved: 1194
[ Submit][ Status][ Discuss]

Description

给定N个点以及每个点的权值,要你处理接下来的M个操作。
操作有4种。操作从0到3编号。点从1到N编号。
0:后接两个整数(x,y),代表询问从x到y的路径上的点的权值的xor和。
保证x到y是联通的。
1:后接两个整数(x,y),代表连接x到y,若x到Y已经联通则无需连接。
2:后接两个整数(x,y),代表删除边(x,y),不保证边(x,y)存在。
3:后接两个整数(x,y),代表将点X上的权值变成Y。

Input

第1行两个整数,分别为N和M,代表点数和操作数。
第2行到第N+1行,每行一个整数,整数在[1,10^9]内,代表每个点的权值。
第N+2行到第N+M+1行,每行三个整数,分别代表操作类型和操作所需的量。
1<=N,M<=300000

Output

对于每一个0号操作,你须输出X到Y的路径上点权的Xor和。

Sample Input

3 3
1
2
3
1 1 2
0 1 2
0 1 1

Sample Output

3
1

HINT

Source




解题思路:时隔半年,再敲动态树,这次更加巩固了。

对于不同的问题,我们只需要修改

pushup函数

Update函数(即Splay树上单点更新)

pushdown函数 即可。


区间查询与区间修改,单点修改看代码即可!这份模板更加清晰易懂!


#include <iostream>
#include <algorithm>
#include <cstdio>
using namespace std;
typedef long long int ll;
const int MAXN = 300095;
int Q[MAXN];	 //Splay用栈
int ch[MAXN][2]; //树节点
int fa[MAXN];	//节点的父亲
int sum[MAXN];   //重链和,即维护的路径信息
int A[MAXN];	 //每一个节点的值
int rev[MAXN];   //翻转标记,维护Splay用
 
 
//判断是否是某个Splay的根节点,旋转用
bool isroot(int rt)
{
    return ch[fa[rt]][0] != rt && ch[fa[rt]][1] != rt;
}
bool get(int x) { return ch[fa[x]][1] == x; }
 
 
//上传函数,维护节点信息
void pushup(int rt)
{
    int l = ch[rt][0];
    int r = ch[rt][1];
    sum[rt] = (sum[l] ^ sum[r] ^ A[rt]);
}
 
 
/*
//给树上节点单点更新,记住这个相当于区间更新,因为树上结点就代表区间信息(Splay树上的节点)
void updateAdd(int rt, int C)
{
    if (!rt)
        return;
    A[rt] = (A[rt] + C) % MOD;
    sum[rt] = (sum[rt] + siz[rt] * C % MOD) % MOD;
    lazyAdd[rt] = (lazyAdd[rt] + C) % MOD;
}
void updateMul(int rt, int C)
{
    if (!rt)
        return;
    A[rt] = A[rt] * C % MOD;
    sum[rt] = sum[rt] * C % MOD;
    lazyAdd[rt] = lazyAdd[rt] * C % MOD;
    lazyMul[rt] = lazyMul[rt] * C % MOD;
}
*/
 
 
//下推函数
void pushdown(int x)
{
    if (rev[x])
    {
        rev[ch[x][0]] ^= 1;
        rev[ch[x][1]] ^= 1;
        rev[x] ^= 1;
        swap(ch[x][0], ch[x][1]);
    }
    
    /*
    if (lazyAdd[rt])
    {
        updateAdd(l, lazyAdd[rt]);
        updateAdd(r, lazyAdd[rt]);
        lazyAdd[rt] = 0;
    }
    if (lazyMul[rt] != 1)
    {
        updateAdd(l, lazyMul[rt]);
        updateAdd(r, lazyMul[rt]);
        lazyMul[rt] = 1;
    }
    */
    
}
 
 
//Splay旋转函数,通常无需修改
void Rotate(int x)
{
    int old = fa[x], oldf = fa[old], op = get(x);
    if (!isroot(old))
        ch[oldf][ch[oldf][1] == old] = x; 
    ch[old][op] = ch[x][op ^ 1];
    fa[ch[x][op ^ 1]] = old; //但这里使用isroot,改变之后就不能判断了!
    ch[x][op ^ 1] = old;
    fa[old] = x;
    fa[x] = oldf;
    pushup(old);
    pushup(x);
}
//Splay函数,将rt变为某棵Splay树的根节点,通常无需修改
void Splay(int x)
{
    int tp = 1;
    Q[1] = x;
    for (int i = x; !isroot(i); i = fa[i])
        Q[++tp] = fa[i]; //对于LCT的判断是否是根节点,需要使用isroot,在纯Splay中使用的是fa,不要搞混!
    for (int i = tp; i; i--)
        pushdown(Q[i]);
    for (int FA; !isroot(x); Rotate(x))
    {
        FA = fa[x];
        if (!isroot(FA))
            Rotate(get(x) == get(FA) ? FA : x);
    }
}
 
 
//打通x到整个LCT的根的路径,即fa和ch都是正确的
void Access(int x)
{
    int t = 0;
    while (x)
    {
        Splay(x);
        ch[x][1] = t;
        pushup(x);
        t = x;
        x = fa[x];
    }
}
//把x变为整个LCT的根,先打通路径,然后把他变为他的Splay的根即可。
void Makeroot(int x)
{
    Access(x);
    Splay(x);
    rev[x] ^= 1;
}
//链接函数,先把x变为整个LCT的根(这时x才没有父亲,所以不能用Splay),然后再设置一个父亲即可
void Link(int x, int y)
{
    Makeroot(x);
    fa[x] = y;
}
//剪断函数,根据Splay原理,可以把x变为y的左节点,然后删除即可
void Cut(int x, int y)
{
    Makeroot(x);
    Access(y);
    Splay(y);
    if (ch[y][0] == x)
        fa[x] = ch[y][0] = 0;
}
//分割函数,即把x到y这条路径变为一颗Splay树,从而把区间查询变为树上节点查询(Splay原理)
void split(int x, int y)
{
    Makeroot(y);
    Access(x);
    Splay(x);
}
 
 
//查询x到y之间是否已经有路径,直接查询所在的LCT的根是否相等即可
int Find(int x)
{
    Access(x);
    Splay(x);
    while (ch[x][0])
        x = ch[x][0];
    return x;
}
 
 
int main()
{
    int N, Q;
    scanf("%d%d", &N, &Q);
    for (int i = 1; i <= N; i++)
    {
        scanf("%d", &A[i]);
        sum[i] = A[i];
    }
    
    int x, y;
    int op;
    while (Q--)
    {
        scanf("%d", &op);
        if (op == 0)
        {
            scanf("%d%d", &x, &y);
            split(x, y); //把x到y这条路径用Splay维护,并且x为根节点(即x包含了整个路径信息)
            printf("%d\n", sum[x]);
        }
        if (op == 1)
        {
            
            scanf("%d%d", &x, &y);
            if (Find(x) != Find(y))
                Link(x, y);
        }
        if (op == 2)
        {
            scanf("%d%d", &x, &y);
            Cut(x, y);
        }
        if (op == 3)
        {
            scanf("%d%d", &x, &y);
            Makeroot(x);
            A[x] = y;
            pushup(x);
        }
    }
    
    return 0;
}
 
 











猜你喜欢

转载自blog.csdn.net/lzc504603913/article/details/80254140