首页 > 其他分享 >P2486 [SDOI2011] 染色 题解

P2486 [SDOI2011] 染色 题解

时间:2023-08-27 22:56:45浏览次数:48  
标签:dep int 题解 top2 top1 SDOI2011 dfn P2486 itr

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;
}

标签:dep,int,题解,top2,top1,SDOI2011,dfn,P2486,itr
From: https://www.cnblogs.com/Multitree/p/17661043.html

相关文章

  • P9585 酒店题解
    分析:贪心算法。有\(n\)个客人。对于每一位客人,我们都要遍历一遍所有房间,找出最优入住房间编号。设当前遍历的房间编号为\(j\)。分三种情况:左右两边的房间皆空,则为最优房间。左右两边只有一个房间有客人,则愤怒值加\(2\)(因为有两个客人所以加\(2\))。左右两边都有人住,则......
  • ABC317F题解
    让人头大的数位DP。建议评蓝。个人认为不适合放ABC的F。将三个数二进制拆分,使三个数异或为0相当于每个二进制位三个数中有0或2个是1。所以考虑数位DP,设\(dp[i][m1][m2][m3][lim1][lim2][lim3]\)为第\(i\)位,三个数模\(a\),\(b\),\(c\)分别是\(m1\),\(m2\),\(m3\)......
  • NOIP2013提高组初赛易错题解析
    7. 正解:可以画出递归树,画出后应该是这样子的 画出递归树,就可以得出答案时间复杂度为O(Fn) 15. 正解:2T(n/2)=O(logn)T(n)=2*T(n/2)+2*n=O(nlogn)三.2. 错误原因:蒙的正解:通过观察,可以找到递推关系式,f[n]=1/n*(n+f[1]+f[2]+...+f[n]),f[1]=0,f[2]=2,经过计算......
  • NOIP2017提高组初赛易错题解析
     8.由四个不同的点构成的简单无向连通图的个数是()A.32 B.35 C.38 D.41错误原因:数重了正解:分情况计算,6条边的有1种,5条边的有C(6,1)=6种,4条边的有C(6,4)=15种,3条边,要分度数,2+2+1+1的有12种,3+1+1+1的有4种,共38种 10.若 f0​=0,f1​=1,fn+1​=(fn​+fn−1)/2​​,则随着......
  • NOIP2016提高组初赛易错题解析
    9. 正解:每一个bit,都有两种可能,0和1,所以最多可以使用232=4GB的内存 14. 正解:使用代入法,T(n)=2T(n/4)+sqrt(n),T(n/16)=2T(n/4/4/4)+1/4*sqrt(n),T(n)=2k+k*sqrt(n)=sqrt(n)+k*sqrt(n),则时间复杂度为O(sqrt(n)logn) 二.1. 正解:前三个都是无线通信技术,以太网是有......
  • NOIP2018提高组初赛易错题解析
    2.下列属于解释执行的程序设计语言是()A.C B.C++ C.Pascal D.Python错误原因:忘记了正解:C、C++和Pascal都是编译性语言,而Python是解释性语言 5.设某算法的时间复杂度函数的递推方程是 T(n)=T(n-1)+n(n 为正整数)及 T(0)=1,则该算法的时间复杂度为()A.O(logn) ......
  • P7414 [USACO21FEB] Modern Art 3 G 题解
    思路考虑区间DP。设\(f_{i,j}\)表示要刷到\([i,j]\)这一段的目标需要的最小次数。对于\(f_{i,j}\),如果\(color_i\)与\(color_j\)相等,那么再子区间合并的时候就可以少刷一次,即\(f_{i,j}=\min\limits_{k=i}^{j-1}f_{i,k}+f_{k+1,j}-1\)。否则\(f......
  • CSP-J2022初赛易错题解析
    7.假设字母表{a,b,c,d,e}在字符串出现的频率分别为10%,15%,30%,16%,29%。若使用哈夫曼编码方式对字母进行不定长的二进制编码,字母d的编码长度()位。A.1  B.2 C.2或3  D.3正解:画出哈夫曼树即可9.考虑由N个顶点构成的有向连通图,采用邻接矩阵的数据结构表示时,该矩阵中至......
  • CSP-J2020初赛易错题解析
    一.5. 正解:冒泡排序最少比较n-1次,即单调上升序列 10.5 个小朋友并排站成一列,其中有两个小朋友是双胞胎,如果要求这两个双胞胎必须相邻,则有()种不同排列方法?A.24 B.36 C.72 D.48错误原因:忘记乘上A(2,2)了正解:捆绑法,A(4,4)*A(2,2)=48 15.有五副不同颜色的手套(......
  • CSP-J2021初赛易错题解析
    12.由 1,1,2,2,3 这五个数字组成不同的三位数有()种。A.18 B.15 C.12 D.24正解:枚举法,枚举即可,共18种 15.有四个人要从A点坐一条船过河到B点,船一开始在A点。该船一次最多可坐两个人。已知这四个人中每个人独自坐船的过河时间分别为 1,2,4,8,且两个人坐船的......