P2486 [SDOI2011] 染色 题解

发布时间 2023-08-27 22:47:52作者: Aisaka_Taiga

P2486 [SDOI2011] 染色

神仙树剖题。

题意

给你一棵树,每个点都有颜色,支持下面两种操作:

  • 路径染色。

  • 路径颜色段数量查询。

树剖部分

我们看到树上问题,不好处理,所以想办法给他树剖搞一搞,给他转化成序列的操作。

我们树剖就是正常的树剖,然后我们考虑如何维护这个颜色序列。

我们一般都是去想用线段树去维护这个东西,但是里面有区间赋值的操作,我们为什么不用珂爱的珂朵莉树呢?

珂朵莉树部分

对于正常的 assign 操作,我们需要先写一个暴力的区间推平操作,然后再用我们树剖的一般套路,两个点来回交换然后处理一条链上的点的颜色。

对于查询操作,我们从题目中了解到,查询的是颜色段的数量,我们要考虑,在正常的树剖套路里,不确定是哪一个链跳到哪一个上,如果要是那样写,有可能路径中不在一条链上的点虽然颜色相同但是被算成了两个颜色段,这个时候我们需要借鉴倍增求 LCA 的过程。

我们开两个变量 \(lasta, lastb\) 来记录从 LCA 到两点的链上的上一个出现的颜色是什么。

我们不能按照正常的树剖从上往下遍历求颜色段个数,因为那样的话,是上面的第一个 set 的元素和上一次的 \(last\) 相比,中间很明显是隔了一块,所以我们应该倒着枚举 set 里的元素。

然后我们需要考虑,最后一个区间的查询,因为是在 LCA 处连接的,所以我们在跑完之后要比较一下 \(lasta\) 是否等于 \(lastb\),如果相等说明 LCA 处的颜色段是可以接起来的,这样我们就可以再给答案减去一。

CODE

加上空行和前面一堆没用的东西,一共是 \(157\) 行,个人认为马蜂可读。

/*
 * @Author: Aisaka_Taiga
 * @Date: 2023-08-27 21:11:45
 * @LastEditTime: 2023-08-27 22:28:29
 * @LastEditors: Aisaka_Taiga
 * @FilePath: \Desktop\P2486.cpp
 * 心比天高,命比纸薄。
 */
#include <bits/stdc++.h>

#define IT set<node>::iterator
#define int long long
#define N 200100
#define endl '\n'

using namespace std;

inline int read(){int x=0,f=1;char ch=getchar();while(!isdigit(ch)){f=ch!='-';ch=getchar();}while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}return f?x:-x;}

struct sb{int v, next;}e[N];
int n, m, a[N], dfn[N], siz[N], son[N], f[N], dep[N], pre[N], top[N], head[N], cnt, tim;
struct node
{
    int l, r;
    mutable int v;
    node(int L, int R = -1, int V = 0): l(L), r(R), v(V) {}
    bool operator < (const node &o) const{return l < o.l;}
};
set<node> s;

inline void add(int u, int v){e[++ cnt] = (sb){v, head[u]}; head[u] = cnt;}

inline void dfs1(int x, int fa)
{
    f[x] = fa;
    siz[x] = 1;
    dep[x] = dep[fa] + 1;
    for(int i = head[x]; i; i = e[i].next)
    {
        int v = e[i].v;
        if(v == fa) continue;
        dfs1(v, x);
        if(siz[v] > siz[son[x]]) son[x] = v;
        siz[x] += siz[v];
    }
    return ;
}

inline void dfs2(int x, int tp)
{
    dfn[x] = ++ tim;
    pre[tim] = x;
    top[x] = tp;
    if(!son[x]) return ;
    dfs2(son[x], tp);
    for(int i = head[x]; i; i = e[i].next)
    {
        int v = e[i].v;
        if(v == f[x] || v == son[x]) continue ;
        dfs2(v, v);
    }
    return ;
}

IT split(int p)
{
    IT it = s.lower_bound(node(p));
    if(it != s.end() && it -> l == p) return it;
    it --;
    int L = it -> l, R = it -> r, V = it -> v;
    s.erase(it);
    s.insert(node(L, p - 1, V));
    return s.insert(node(p, R, V)).first;
}

inline void assign(int l, int r, int v)
{
    IT itr = split(r + 1), itl = split(l);
    s.erase(itl, itr);
    s.insert(node(l, r, v));
    return ;
}

inline int ask(int l, int r, int &last)
{
    int res = 0;
    IT itr = split(r + 1), itl = split(l);
    itr --;
    for(; ; itr --)
    {
        if(itr -> v != last) res ++, last = itr -> v;
        if(itl == itr) break;
    }
    return res;
}

inline void Assign(int x, int y, int v)
{
    int top1 = top[x], top2 = top[y];
    while(top1 != top2)
    {
        if(dep[top1] < dep[top2]) swap(top1, top2), swap(x, y);
        assign(dfn[top1], dfn[x], v);
        x = f[top1], top1 = top[x];
    }
    if(dep[x] < dep[y]) swap(x, y);
    assign(dfn[y], dfn[x], v);
    return ;
}

inline int Ask(int x,int y)
{
	int res = 0, lasta = 0, lastb = 0;
	IT itl, itr;
    int top1 = top[x], top2 = top[y];
	while(top1 != top2)
	{
		if(dep[top1] > dep[top2]) res += ask(dfn[top1], dfn[x], lasta), x = f[top1], top1 = top[x];
		else res += ask(dfn[top2], dfn[y], lastb), y = f[top2], top2 = top[y];
	}
	if(dep[x] > dep[y]) res += ask(dfn[y], dfn[x], lasta);
	else res += ask(dfn[x], dfn[y], lastb);
	return res - (lasta == lastb);
}


signed main()
{
    n = read(), m = read();
    for(int i = 1; i <= n; i ++) a[i] = read();
    for(int i = 1; i <= n - 1; i ++)
    {
        int u = read(), v = read();
        add(u, v);
        add(v, u);
    }
    dfs1(1, 0);
    dfs2(1, 1);
    for(int i = 1; i <= n; i ++)
        s.insert(node(dfn[i], dfn[i], a[i]));
    for(int i = 1; i <= m; i ++)
    {
        char ss;
        cin >> ss;
        if(ss == 'Q')
        {
            int x = read(), y = read();
            cout << Ask(x, y) << endl;
        }
        if(ss == 'C')
        {
            int x = read(), y = read(), v = read();
            Assign(x, y, v);
        }
    }
    return 0;
}