题目链接

题目

题目描述

给定一棵有n个节点的无根树和m个操作,操作有2类:

1、将节点a到节点b路径上所有点都染成颜色c;

2、询问节点a到节点b路径上的颜色段数量(连续相同颜色被认为是同一段),如“112221” 由3段组成:“11” 、“222” 和“1” 。

请你写一个程序依次完成这m个操作。

输入描述

第一行包含2个整数n和m,分别表示节点数和操作数;

第二行包含n个正整数表示n个节点的初始颜色下面行每行包含两个整数x和y,表示x和y之间有一条无向边。

下面行每行描述一个操作:

“C a b c”表示这是一个染色操作,把节点a到节点b路径上所有点(包括a和b)都染成颜色c;

“Q a b”表示这是一个询问操作,询问节点a到节点b(包括a和b)路径上的颜色段数量。

输出描述

对于每个询问操作,输出一行答案。

示例1

输入

6 5
2 2 1 2 1 1
1 2
1 3
2 4
2 5
2 6
Q 3 5
C 2 1 1
Q 3 5
C 5 1 2
Q 3 5

输出

3
1
2

备注

对于100 的数据,\(1 \leq n, m \leq 10^5\) ,\(1 \leq w_i, c \leq 10^9\),\(1 \leq a, b, u, v \leq n\) ,op 一定为 C 或 Q,保证给出的图是一棵树。除原数据外,还存在一组不计分的 hack 数据。

题解

知识点:树链剖分,线段树。

与路径查询修改有关,显然需要树链剖分。

颜色段问题可以用懒标记线段树简单处理。注意如果合并时,两段线段的合并处颜色相同,那么段数为段数和减 \(1\) ,否则为段数和。

其中,一个关键是使用树剖查询时,需要注意合并方向,因为颜色段的左右端点是需要区别的。因此,我们可以开一个数组分别记录左右的答案。为了配合查询时的 \(u,v\) 交换操作,采用一个变量 \(pos\) 表示当前 \(u\) 处在初始的左还是右, \(ans[pos]\) 就表示 \(u\) 那一段的答案。最后一次合并时,涉及三段的合并,注意其中一段是需要反转的,将端点颜色交换即可。

时间复杂度 \(O(n \log n + m \log ^2 n)\)

空间复杂度 \(O(n)\)

代码

#include <bits/stdc++.h>
using namespace std;
using ll = long long; struct HLD {
vector<int> siz, dep, fat, son, top, dfn, L, R; HLD() {}
HLD(int rt, const vector<vector<int>> &g) { init(rt, g); }
void init(int rt, const vector<vector<int>> &g) {
assert(g.size() > rt);
int n = g.size() - 1;
siz.assign(n + 1, 0);
dep.assign(n + 1, 0);
fat.assign(n + 1, 0);
son.assign(n + 1, 0);
top.assign(n + 1, 0);
dfn.assign(n + 1, 0);
L.assign(n + 1, 0);
R.assign(n + 1, 0); function<void(int, int)> dfsA = [&](int u, int fa) {
siz[u] = 1;
dep[u] = dep[fa] + 1;
fat[u] = fa;
for (auto v : g[u]) {
if (v == fa) continue;
dfsA(v, u);
siz[u] += siz[v];
if (siz[v] > siz[son[u]]) son[u] = v;
}
};
dfsA(rt, 0); int dfncnt = 0;
function<void(int, int)> dfsB = [&](int u, int tp) {
top[u] = tp;
dfn[++dfncnt] = u;
L[u] = dfncnt;
if (son[u]) dfsB(son[u], tp);
for (auto v : g[u]) {
if (v == fat[u] || v == son[u]) continue;
dfsB(v, v);
}
R[u] = dfncnt;
};
dfsB(rt, rt);
}
}; template<class T, class F>
class SegmentTreeLazy {
int n;
vector<T> node;
vector<F> lazy; void push_down(int rt) {
node[rt << 1] = lazy[rt](node[rt << 1]);
lazy[rt << 1] = lazy[rt](lazy[rt << 1]);
node[rt << 1 | 1] = lazy[rt](node[rt << 1 | 1]);
lazy[rt << 1 | 1] = lazy[rt](lazy[rt << 1 | 1]);
lazy[rt] = F();
} void update(int rt, int l, int r, int x, int y, F f) {
if (r < x || y < l) return;
if (x <= l && r <= y) return node[rt] = f(node[rt]), lazy[rt] = f(lazy[rt]), void();
push_down(rt);
int mid = l + r >> 1;
update(rt << 1, l, mid, x, y, f);
update(rt << 1 | 1, mid + 1, r, x, y, f);
node[rt] = node[rt << 1] + node[rt << 1 | 1];
} T query(int rt, int l, int r, int x, int y) {
if (r < x || y < l) return T();
if (x <= l && r <= y) return node[rt];
push_down(rt);
int mid = l + r >> 1;
return query(rt << 1, l, mid, x, y) + query(rt << 1 | 1, mid + 1, r, x, y);
} public:
SegmentTreeLazy(int _n = 0) { init(_n); }
SegmentTreeLazy(const vector<T> &src) { init(src); } void init(int _n) {
n = _n;
node.assign(n << 2, T());
lazy.assign(n << 2, F());
}
void init(const vector<T> &src) {
assert(src.size() >= 2);
init(src.size() - 1);
function<void(int, int, int)> build = [&](int rt, int l, int r) {
if (l == r) return node[rt] = src[l], void();
int mid = l + r >> 1;
build(rt << 1, l, mid);
build(rt << 1 | 1, mid + 1, r);
node[rt] = node[rt << 1] + node[rt << 1 | 1];
};
build(1, 1, n);
} void update(int x, int y, F f) { update(1, 1, n, x, y, f); } T query(int x, int y) { return query(1, 1, n, x, y); }
}; struct T {
int lc = 0, rc = 0;
int cnt = 0;
friend T operator+(const T &a, const T &b) {
if (!a.cnt) return b;
if (!b.cnt) return a;
return {
a.lc, b.rc,
a.cnt + b.cnt - (a.rc == b.lc)
};
}
}; struct F {
int upd;
T operator()(const T &x) {
if (!upd) return x;
return {
upd, upd,
1,
};
}
F operator()(const F &g) {
if (!upd) return g;
return { upd };
}
}; const int N = 100007;
int c[N];
vector<int> g[N]; HLD hld;
SegmentTreeLazy<T, F> sgt; void path_update(int u, int v, int w) {
auto &top = hld.top;
auto &dep = hld.dep;
auto &L = hld.L;
auto &fat = hld.fat;
while (top[u] != top[v]) {
if (dep[top[u]] < dep[top[v]]) swap(u, v);
sgt.update(L[top[u]], L[u], { w });
u = fat[top[u]];
}
if (dep[u] > dep[v]) swap(u, v);
sgt.update(L[u], L[v], { w });
} int path_query(int u, int v) {
auto &top = hld.top;
auto &dep = hld.dep;
auto &L = hld.L;
auto &fat = hld.fat;
bool pos = 0;
T ans[2] = { T(),T() };
while (top[u] != top[v]) {
if (dep[top[u]] < dep[top[v]]) swap(u, v), pos ^= 1;
ans[pos] = sgt.query(L[top[u]], L[u]) + ans[pos];
u = fat[top[u]];
}
if (dep[u] > dep[v]) swap(u, v), pos ^= 1;
swap(ans[pos].lc, ans[pos].rc);
return (ans[pos] + sgt.query(L[u], L[v]) + ans[pos ^ 1]).cnt;
} int main() {
std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int n, m;
cin >> n >> m;
for (int i = 1;i <= n;i++) cin >> c[i];
for (int i = 1;i <= n - 1;i++) {
int u, v;
cin >> u >> v;
g[u].push_back(v);
g[v].push_back(u);
} hld.init(1, vector<vector<int>>(g, g + n + 1));
vector<T> c_src(n + 1);
for (int i = 1;i <= n;i++) c_src[hld.L[i]] = { c[i],c[i],1 };
sgt.init(c_src); while (m--) {
char op;
int a, b;
cin >> op >> a >> b;
if (op == 'C') {
int c;
cin >> c;
path_update(a, b, c);
}
else cout << path_query(a, b) << '\n';
}
return 0;
}

NC20573 [SDOI2011]染色的更多相关文章

  1. BZOJ 2243: [SDOI2011]染色 [树链剖分]

    2243: [SDOI2011]染色 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 6651  Solved: 2432[Submit][Status ...

  2. bzoj-2243 2243: [SDOI2011]染色(树链剖分)

    题目链接: 2243: [SDOI2011]染色 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 6267  Solved: 2291 Descript ...

  3. 【BZOJ2243】[SDOI2011]染色 树链剖分+线段树

    [BZOJ2243][SDOI2011]染色 Description 给定一棵有n个节点的无根树和m个操作,操作有2类: 1.将节点a到节点b路径上所有点都染成颜色c: 2.询问节点a到节点b路径上的 ...

  4. bzoj2243:[SDOI2011]染色

    链剖就可以了.一开始的想法错了.但也非常接近了.妈呀调的要死...然后把字体再缩小一号查错起来比较容易QAQ. #include<cstdio> #include<cstring&g ...

  5. bzoj 2243 [SDOI2011]染色(树链剖分,线段树)

    2243: [SDOI2011]染色 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 4637  Solved: 1726[Submit][Status ...

  6. Bzoj 2243: [SDOI2011]染色 树链剖分,LCT,动态树

    2243: [SDOI2011]染色 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 5020  Solved: 1872[Submit][Status ...

  7. 2243: [SDOI2011]染色

    2243: [SDOI2011]染色 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 3113  Solved: 1204[Submit][Status ...

  8. bzoj 2243 [SDOI2011]染色(树链剖分+线段树合并)

    [bzoj2243][SDOI2011]染色 2017年10月20日 Description 给定一棵有n个节点的无根树和m个操作,操作有2类: 1.将节点a到节点b路径上所有点都染成颜色c: 2.询 ...

  9. [SDOI2011]染色

    [SDOI2011]染色 题目描述 输入输出格式 输出格式: 对于每个询问操作,输出一行答案. 解法 ps:这题本来是树剖的,但我用lct写的,以下是lct的写法,树剖会有所不同 我们考虑把不同色点的 ...

  10. bzoj2243[SDOI2011]染色 树链剖分+线段树

    2243: [SDOI2011]染色 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 9012  Solved: 3375[Submit][Status ...

随机推荐

  1. 15-TTL与非门

    TTL与非门 集成电路有两大类COMOS和TTL(三极管) 电路结构 工作原理 多发射结的三极管,两个输入之间是与的关系 输入低电平 输入高电平 A.B都是高电平 倒置放大 压差大的先导通 T3,T4 ...

  2. 【收集】embed

    序 链接 备注 1 适用于嵌入式单片机的差分升级通用库+详细教程_风吹花中花吹风的博客-CSDN博客 差分升级包制作与还原 2 前言 · 给新手的两轮自平衡小车开发实战指南 (miaowlabs.co ...

  3. [转帖]【我和CloudQuery 的故事】安装部署CloudQuery 初体验—-前篇

    https://www.modb.pro/db/1694256553947910144 一.前言 在日常数据库运维中,为连接多种数据库,经常要安装不同的客户端,非常繁琐,且占用大量存储空间.如果能有一 ...

  4. [转帖]Java程序在K8S容器部署CPU和Memory资源限制相关设置

    2019-04-297279 版权 本文涉及的产品 容器服务 Serverless 版 ACK Serverless,317元额度 多规格 推荐场景: 立即试用 容器镜像服务 ACR,镜像仓库100个 ...

  5. [转帖]minio 的 warp

    3 benchmarking tool. Download Download Binary Releases for various platforms. Configuration Warp can ...

  6. [转帖]Intel固态硬盘总结

    https://www.cnblogs.com/hongdada/p/17326247.html 2012年推出的S3700,采用的是25nm闪存颗粒. 2015年推出s3710,采用的是20nm闪存 ...

  7. 物理机和虚拟机上CPU睿频的区别

    物理机和虚拟机上CPU睿频的区别 关于睿频 睿频是指当启动一个运行程序后,处理器会自动加速到合适的频率, 而原来的运行速度会提升 10%~20% 以保证程序流畅运行的一种技术. 一般max的睿频不能超 ...

  8. CentOS8 设置开机自动登录账户的方法

    CentOS8 设置开机自动登录账户的方法 修改/etc/gdm/custom.conf文件, 并且添加内容即可. vim /etc/gdm/custom.conf # 在配置节下添加如下内容. [d ...

  9. elementui 的tabs组件出现蓝色边框问题

    elementui 的tabs组件出现蓝色边框问题 /deep/ .el-tabs__item:focus.is-active.is-focus:not(:active) { -webkit-box- ...

  10. 【JS 逆向百例】无限debugger绕过,某政民互动数据逆向

    声明 本文章中所有内容仅供学习交流,抓包内容.敏感网址.数据接口均已做脱敏处理,严禁用于商业用途和非法用途,否则由此产生的一切后果均与作者无关,若有侵权,请联系我立即删除! 逆向目标 目标:某政务服务 ...