洛谷P2486 lct做法

题意:

给出一颗 n n n个结点的无根树,处理 m m m个操作,每个操作只会是如下两种之一:

(1)将结点 a a a b b b的路径上的所有点染成颜色 k k k

(2)询问结点 a a a到结点 b b b的简单路径的颜色段数量

LCT方法:
L C T LCT LCT是用来维护动态增删边和树上路径的数据结构
对于每个 s p l a y splay splay,维护的是一条链,节点 x x x在原树连接的是他在 s p l a y splay splay的前驱,不是 s p l a y splay splay的儿子, x x x的左子树是他之前的链,右子树是之后的链

对于路径 ( x , y ) (x,y) (x,y),先 s p l i t ( x , y ) split(x,y) split(x,y)

我们先考虑查询, s u m sum sum保存当前 s p l a y splay splay的“连接处”的个数,即相邻颜色不一样的节点对数,答案即 t r e e [ y ] . s u m + 1 tree[y].sum+1 tree[y].sum+1,那么问题就到了如何在 p u s h _ u p push\_up push_up中维护这个值,显然,对节点 x x x t r e e [ x ] . s u m = t r e e [ s o n [ 0 ] ] . s u m + t r e e [ s o n [ 1 ] ] . s u m + x tree[x].sum=tree[son[0]].sum+tree[son[1]].sum+x tree[x].sum=tree[son[0]].sum+tree[son[1]].sum+x节点对他前后两段形成的“连接处个数”,一种方法是找前驱,即用传统 s p l a y splay splay的方法,但在 L C T LCT LCT内由于 p r e ( ) pre() pre()会调用 s p l a y ( ) splay() splay() s p l a y ( ) splay() splay()会调用$push_up()
, , push_up 会调用 会调用 会调用pre() ,循环递归,会出现错误。另一种方法是新增成员,来保存当前 ,循环递归,会出现错误。另一种方法是新增成员,来保存当前 ,循环递归,会出现错误。另一种方法是新增成员,来保存当前splay 子树的最左端点和最右端点,这样贡献即 子树的最左端点和最右端点,这样贡献即 子树的最左端点和最右端点,这样贡献即tree[x].color!=tree[tree[x].son[i]].colorr[i] ,此时 ,此时 ,此时reverse() 也需要反转 也需要反转 也需要反转tree[x].colorr[i]$

修改操作只需 s p l i t ( x , y ) split(x,y) split(x,y)之后在 y y y处下放标记即可了

#include<bits/stdc++.h>
#define ll long long
using namespace std;

int n,q;

struct LCT
{
    struct node
    {
        int color,tag,sum,son[2],fa,lazy,colorr[2];
    };
    stack<int>s;
    vector<node>tree;
    LCT():tree(200005){}
    inline bool isroot(int x)
    {
        return tree[tree[x].fa].son[0]!=x&&tree[tree[x].fa].son[1]!=x;
    }
    inline int getson(int x,int y)
    {
        return x==tree[y].son[1];
    }
    inline void push_up(int x)
    {
        tree[x].sum=0;
		for(int i=0;i<=1;i++)
		{
			if(tree[x].son[i])
			{
				tree[x].sum+=tree[tree[x].son[i]].sum+(tree[x].color!=tree[tree[x].son[i]].colorr[!i]);
				tree[x].colorr[i]=tree[tree[x].son[i]].colorr[i];
			}
			else tree[x].colorr[i]=tree[x].color;
		}
    }
    inline void rotate(int x)
    {
        int y=tree[x].fa,z=tree[y].fa;
        int k1=getson(x,y),k2=getson(y,z);
        tree[x].fa=z;
        if(!isroot(y)) tree[z].son[k2]=x;
        tree[y].son[k1]=tree[x].son[!k1];
        if(tree[x].son[!k1]) tree[tree[x].son[!k1]].fa=y;
        tree[x].son[!k1]=y;
        tree[y].fa=x;
        push_up(y); push_up(x);
    }
    inline void reverse(int x)
    {
        swap(tree[x].son[0],tree[x].son[1]);
		swap(tree[x].colorr[0],tree[x].colorr[1]);
        tree[x].tag^=1;
    }
    inline void ccolor(int x,int k)
    {
        tree[x].lazy=k;
        tree[x].color=k;
        tree[x].sum=0;
		tree[x].colorr[0]=tree[x].colorr[1]=k;
    }
    void push_down(int x)
    {
        if(tree[x].tag)
        {
            for(int i=0;i<=1;i++)
                if(tree[x].son[i]) reverse(tree[x].son[i]);
            tree[x].tag=0;
        }
        if(tree[x].lazy)
        {
            for(int i=0;i<=1;i++)
                if(tree[x].son[i]) ccolor(tree[x].son[i],tree[x].lazy);
            tree[x].lazy=0;
        }
    }
    void splay(int x)
    {
        int now=x;
        s.push(now);
        while(!isroot(now))
        {
            s.push(tree[now].fa);
            now=tree[now].fa;
        }
        while(!s.empty())
        {
            push_down(s.top());
            s.pop();
        }
        while(!isroot(x))
        {
            int y=tree[x].fa,z=tree[y].fa;
            int k1=getson(x,y),k2=getson(y,z);
            if(!isroot(y))
            {
                if(k1==k2) rotate(y);
                else rotate(x);
            }
            rotate(x);
        }
    }
    inline void access(int x)//打通root->x的splay
    {
        int last=0;
        while(x)
        {
            splay(x);
            tree[x].son[1]=last;
            push_up(last=x);
            x=tree[x].fa;
        }
    }
    inline void makeroot(int x)
    {
        access(x);
        splay(x);
        reverse(x);
    }
    inline int findroot(int x)
    {
        access(x);
        splay(x);
        while(tree[x].son[0])
        {
            push_down(x);
            x=tree[x].son[0];
        }
        splay(x);
        return x;
    }
    inline void split(int x,int y)
    {
        makeroot(x);
        access(y);
        splay(y);
    }
    inline void link(int x,int y)
    {
        makeroot(x);
        tree[x].fa=y;
    }
}lct;

int main()
{
    cin>>n>>q;
    int nn=n;
    for(int i=1;i<=n;i++) scanf("%d",&lct.tree[i].color);
    for(int i=1;i<n;i++)
    {
        int u,v; scanf("%d%d",&u,&v);
        lct.link(u,v);
    }
    while(q--)
    {
        // lct.dfs();
        char s[5]; scanf("%s",s+1);
        if(s[1]=='Q')
        {
            int x,y; scanf("%d%d",&x,&y);
            lct.split(x,y);
            printf("%d\n",lct.tree[y].sum+1);
        }
        else
        {
            int x,y,k; scanf("%d%d%d",&x,&y,&k);
            lct.split(x,y); lct.ccolor(y,k);
        }
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值