BZOJ 2243 染色 树链剖分
题意:
给出一棵树,每个顶点上有个颜色\(c_i\)。
有两种操作:
- C a b c 将\(a \to b\)的路径所有顶点上的颜色变为c
- Q a b 查询\(a \to b\)的路径上的颜色段数,连续相同颜色视为一段
分析:
首先树链剖分,下面考虑线段树部分:
我们维护一个区间的左端点的颜色和右断点的颜色以及该区间的颜色段数,在加一个颜色覆盖标记。
在pushup的时候,如果左区间右端点颜色和右区间左端点颜色相同,那么这段颜色可以合并,合并区间的颜色段数为左右子区间颜色段数之和减1;
否则,答案为左右子区间颜色段数之和。
本题的特殊性在于区间合并的顺序性,我们是自底向上将两个顶点跳到\(LCA\)的。因为在每条重链上,顶点在线段树上的编号是从上到下递增的。所以每个子查询得到的区间信息也是从上到下的。我们可以将所得区间左右翻转(具体就是交换区间左右端点颜色,颜色段数不会变)一下,再合并最终得到整个查询区间。
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn = 100000 + 10;
const int maxnode = maxn * 4;
struct Edge
{
int v, nxt;
Edge() {}
Edge(int v, int nxt):v(v), nxt(nxt) {}
};
int n, m, a[maxn];
int ecnt, head[maxn];
Edge edges[maxn * 2];
void AddEdge(int u, int v) {
edges[ecnt] = Edge(v, head[u]);
head[u] = ecnt++;
edges[ecnt] = Edge(u, head[v]);
head[v] = ecnt++;
}
int fa[maxn], dep[maxn], sz[maxn], son[maxn];
int tot, top[maxn], id[maxn], pos[maxn];
void dfs(int u) {
sz[u] = 1; son[u] = 0;
for(int i = head[u]; ~i; i = edges[i].nxt) {
int v = edges[i].v;
if(v == fa[u]) continue;
dep[v] = dep[u] + 1;
fa[v] = u;
dfs(v);
sz[u] += sz[v];
if(sz[v] > sz[son[u]]) son[u] = v;
}
}
void dfs2(int u, int tp) {
id[u] = ++tot;
pos[tot] = u;
top[u] = tp;
if(!son[u]) return;
dfs2(son[u], tp);
for(int i = head[u]; ~i; i = edges[i].nxt) {
int v = edges[i].v;
if(v == fa[u] || v == son[u]) continue;
dfs2(v, v);
}
}
struct Node
{
int lcol, rcol, cntv;
Node() {}
Node(int l, int r, int c): lcol(l), rcol(r), cntv(c) {}
};
void reverse(Node& t) { swap(t.lcol, t.rcol); }
Node operator + (const Node& a, const Node& b) {
if(!a.cntv) return b; if(!b.cntv) return a;
return Node(a.lcol, b.rcol, a.cntv + b.cntv - 1 + (a.rcol != b.lcol));
}
int setv[maxnode];
Node t[maxnode];
void build(int o, int L, int R) {
setv[o] = -1;
if(L == R) {
t[o].cntv = 1;
t[o].lcol = t[o].rcol = a[pos[L]];
return;
}
int M = (L + R) / 2;
build(o<<1, L, M);
build(o<<1|1, M+1, R);
t[o] = t[o<<1] + t[o<<1|1];
}
void pushdown(int o) {
if(setv[o] != -1) {
setv[o<<1] = setv[o<<1|1] = setv[o];
t[o<<1].cntv = t[o<<1|1].cntv = 1;
t[o<<1].lcol = t[o<<1].rcol = t[o<<1|1].lcol = t[o<<1|1].rcol = setv[o];
setv[o] = -1;
}
}
void update(int o, int L, int R, int qL, int qR, int v) {
if(qL <= L && R <= qR) {
t[o].lcol = t[o].rcol = setv[o] = v;
t[o].cntv = 1;
return;
}
pushdown(o);
int M = (L + R) / 2;
if(qL <= M) update(o<<1, L, M, qL, qR, v);
if(qR > M) update(o<<1|1, M+1, R, qL, qR, v);
t[o] = t[o<<1] + t[o<<1|1];
}
void UPDATE(int u, int v, int val) {
int t1 = top[u], t2 = top[v];
while(t1 != t2) {
if(dep[t1] < dep[t2]) { swap(u, v); swap(t1, t2); }
update(1, 1, n, id[t1], id[u], val);
u = fa[t1]; t1 = top[u];
}
if(dep[u] > dep[v]) swap(u, v);
update(1, 1, n, id[u], id[v], val);
}
Node query(int o, int L, int R, int qL, int qR) {
Node ans(0, 0, 0);
if(qL <= L && R <= qR) return t[o];
pushdown(o);
int M = (L + R) / 2;
if(qL <= M) ans = ans + query(o<<1, L, M, qL, qR);
if(qR > M) ans = ans + query(o<<1|1, M+1, R, qL, qR);
return ans;
}
int QUERY(int u, int v) {
Node q1(0, 0, 0), q2(0, 0, 0), tmp;
int t1 = top[u], t2 = top[v];
while(t1 != t2) {
if(dep[t1] > dep[t2]) {
tmp = query(1, 1, n, id[t1], id[u]);
reverse(tmp);
q1 = q1 + tmp;
u = fa[t1]; t1 = top[u];
} else {
tmp = query(1, 1, n, id[t2], id[v]);
reverse(tmp);
q2 = q2 + tmp;
v = fa[t2]; t2 = top[v];
}
}
if(dep[u] > dep[v]) {
tmp = query(1, 1, n, id[v], id[u]);
reverse(tmp);
q1 = q1 + tmp;
} else {
tmp = query(1, 1, n, id[u], id[v]);
reverse(tmp);
q2 = q2 + tmp;
}
reverse(q2);
q1 = q1 + q2;
return q1.cntv;
}
int main()
{
scanf("%d%d", &n, &m);
for(int i = 1; i <= n; i++) scanf("%d", a + i);
ecnt = 0;
memset(head, -1, sizeof(head));
for(int i = 1; i < n; i++) {
int u, v; scanf("%d%d", &u, &v);
AddEdge(u, v);
}
dfs(1);
tot = 0;
dfs2(1, 1);
build(1, 1, n);
char cmd[5];
int a, b, c;
while(m--) {
scanf("%s", cmd);
scanf("%d%d", &a, &b);
if(cmd[0] == 'C') {
scanf("%d", &c);
UPDATE(a, b, c);
} else {
printf("%d\n", QUERY(a, b));
}
}
return 0;
}
BZOJ 2243 染色 树链剖分的更多相关文章
- BZOJ 2243 染色 | 树链剖分模板题进阶版
BZOJ 2243 染色 | 树链剖分模板题进阶版 这道题呢~就是个带区间修改的树链剖分~ 如何区间修改?跟树链剖分的区间询问一个道理,再加上线段树的区间修改就好了. 这道题要注意的是,无论是线段树上 ...
- BZOJ - 2243 染色 (树链剖分+线段树+区间合并)
题目链接 线段树维护区间连续段个数即可.设lc为区间左端点颜色,rc为区间右端点颜色,则合并两区间的时候,如果左区间右端点和右区间左端点颜色相同,则连续段个数-1. 在树链上的区间合并可以定义一个结构 ...
- hysbz 2243 染色(树链剖分)
题目链接:hysbz 2243 染色 题目大意:略. 解题思路:树链剖分+线段树的区间合并,可是区间合并比較简单,节点仅仅要记录左右端点的颜色就可以. #include <cstdio> ...
- HYSBZ - 2243 染色 (树链剖分+线段树)
题意:树上每个结点有自己的颜色,支持两种操作:1.将u到v路径上的点颜色修改为c; 2.求u到v路径上有多少段不同的颜色. 分析:树剖之后用线段树维护区间颜色段数.区间查询区间修改.线段树结点中维护的 ...
- BZOJ 2243: [SDOI2011]染色 [树链剖分]
2243: [SDOI2011]染色 Time Limit: 20 Sec Memory Limit: 512 MBSubmit: 6651 Solved: 2432[Submit][Status ...
- Bzoj 2243: [SDOI2011]染色 树链剖分,LCT,动态树
2243: [SDOI2011]染色 Time Limit: 20 Sec Memory Limit: 512 MBSubmit: 5020 Solved: 1872[Submit][Status ...
- BZOJ 2243: [SDOI2011]染色 树链剖分 倍增lca 线段树
2243: [SDOI2011]染色 Time Limit: 20 Sec Memory Limit: 256 MB 题目连接 http://www.lydsy.com/JudgeOnline/pr ...
- BZOJ 2243: [SDOI2011]染色 树链剖分+线段树区间合并
2243: [SDOI2011]染色 Description 给定一棵有n个节点的无根树和m个操作,操作有2类: 1.将节点a到节点b路径上所有点都染成颜色c: 2.询问节点a到节点b路径上的颜色段数 ...
- BZOJ 2243: [SDOI2011]染色 (树链剖分+线段树合并)
题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=2243 树链剖分的点剖分+线段树.漏了一个小地方,调了一下午...... 还是要细心啊! 结 ...
随机推荐
- 有关JSOUP学习分享(一)
其实现在用JSOUP爬虫的也不多了,但是由于最近换公司,做数据爬虫需要用到,就看了下,感觉还是挺好用的,原理什么的感觉和weblogic也差不到哪里去,废话少说,这里就简单的分享下最近接触的干货. J ...
- 【Unity3D】点击交互——简单工厂
实现一个很简单的点击小游戏,学习交互相关的内容,在不实时创建销毁的情况下,使用简单工厂创建.管理.回收.复用标记. 游戏概述:点击出现标记,两秒内自动消失 游戏展示: 1.1实现点击效果. 1.1.1 ...
- web标准、可用性、可访问性
前言:大家不难发现,只要是招聘UED相关的岗位,如前端开发工程师.交互设计师.用户研究员甚至视觉设计师,一般都对web标准.可用性和可访问性的理解有要求.那么到底什么是web标准.可用性.可访问性呢? ...
- 零基础逆向工程23_PE结构07_重定位表_IAT表(待补充)
重定位表 待补充 IAT表 待补充
- xcode在代码中查找中文
总是忘记xcode中查找中文,这次记下来,以后就不会忘记了,哈哈 请看下图: 切换到查找,点击find后面的text,选择Regular Expression,然后输入 1. 查找非ascii的字符 ...
- C++拾遗(五)——类
类是 C++ 中最重要的特征.C++ 语言的早期版本被命名为“带类的 C(Cwith Classes)”,以强调类机制的中心作用.随着语言的演变,创建类的配套支持也在不断增加.语言设计的主要目标也变成 ...
- LR脚本示例之常用函数
1.变量和参数的设置 //将IP地址和端口放入到参数中lr_save_string("127.0.0.1:1080","ip"); //退出脚本建议使用lr_e ...
- python os,sys模块的使用
首先,os模块是用来与操作系统进行交互的模块,可以对操作系统上的一些东西进行操作 而sys是用来对解释器进行一些操作的 一.os os.getcwd() 获取当前工作目录,即当前python脚本工作的 ...
- exportfs: /mnt/demo requires fsid= for NFS export
解决方法:/mnt/demo 10.0.1.57(fsid=0,rw,async) //加入fsid=0参数就可.
- LeetCode 53题 最大子序和 -- JavaScript
解题思路分析: 该题是在一个整数数组中找到一个和最大的连续子数组,并返回和值.那么如何找到一个和最大的连续子数组呢?我们知道,这肯定需要遍历数组才行:好,那我们就开始遍历数组.首先,我们初始化最大和 ...