LCT强强!以前总是觉得LCT非常的难懂(当然现在也是的),但实际上它真的是很厉害的一种东西。它是一种动态的链剖分结构,其实就是对于剖分出来的重链使用LCT去进行维护。cut 与 link 两个操作让我们可以构造出希望存在的链(动态更新),而 split 操作则可以提取出任意一条从 \(u\) 到 \(v\) 的链使得这条链成为重链,也就是处于一棵 splay 当中,并用根节点来返回信息。可以说,大部分与链有关的问题都可以考虑使用LCT来求解。

  那么这道题乍一看1操作十分的棘手,但如果和LCT联想的话会发现实际上这就是一个 access 的操作,让一个节点到根的路径成为重链并断开原有的重儿子。这样一个条路径上的颜色个数,就是经过的虚边的条数。对于操作2,实际上是一个 LCT 的模板应用。我们维护颜色段的信息,用 \(LC[u]\) 和 \(RC[u]\) 分别代表以\(u\) 为根节点在 splay 上的子树的最右和最左两个节点的颜色。这样就可以维护了。修改颜色使用一个标记即可。

  而3操作我们可以发现:在 access 的时候,我们每修改一条虚边为实边,就会断开一条原本为实边的边为虚边。这样对于它们子树的贡献分别是 \(-1, +1\)。我们在线段树上维护一下加减值即可。不过要注意由于3操作中的 access 已经用于维护树的形态了,我们就不能再随意的去变动它。所以为了实现2和3操作,我们必须使用两棵LCT。以及在翻转子树的时候,\(LC[u]\) 和 \(RC[u]\) 都必须要翻转!

  这里我也有一个不是很理解的地方。标记一般来说有两种,一种打了标记表示自身及子树还未被修改,另一种则表示自身已经被修改,子树还未被修改。在以往我写的 LCT 中,翻转标记用第一种来维护完全没有问题。可以加入了覆盖的标记之后,只有第2种才是正确的。我也不是很懂为什么……如果有知道的,还请评论 \ 私信我一下好吗QAQ

  加之以前我一直以为LCT只能维护链信息,但老师说也是可以维护子树信息的,只要在添加虚边的时候更新一下即可,但这个必须要子树贡献满足可加性才行。

#include <bits/stdc++.h>
using namespace std;
#define maxn 1000000
int n, m, timer, size[maxn];
int id[maxn], dfn[maxn], dep[maxn]; int read()
{
int x = , k = ;
char c;
c = getchar();
while(c < '' || c > '') { if(c == '-') k = -; c = getchar(); }
while(c >= '' && c <= '') x = x * + c - '', c = getchar();
return x * k;
} struct edge
{
int cnp, to[maxn], last[maxn], head[maxn];
edge() { cnp = ; }
void add(int u, int v)
{
to[cnp] = v, last[cnp] = head[u], head[u] = cnp ++;
to[cnp] = u, last[cnp] = head[v], head[v] = cnp ++;
}
}E1; struct Segament_Tree
{
int mark[maxn], mx[maxn]; void Build(int p, int l, int r)
{
if(l == r) { mx[p] = dep[id[l]]; return; }
int mid = (l + r) >> ;
Build(p << , l, mid), Build(p << | , mid + , r);
mx[p] = max(mx[p << ], mx[p << | ]);
} void push_down(int u)
{
if(!mark[u]) return;
mark[u << ] += mark[u]; mark[u << | ] += mark[u];
mx[u << ] += mark[u], mx[u << | ] += mark[u];
mark[u] = ;
} void update(int p, int l, int r, int L, int R, int x)
{
if(L <= l && R >= r)
{
mark[p] += x; mx[p] += x;
return;
}
if(L > r || R < l) return;
int mid = (l + r) >> ;
push_down(p);
update(p << , l, mid, L, R, x); update(p << | , mid + , r, L, R, x);
mx[p] = max(mx[p << ], mx[p << | ]);
} int query(int p, int l, int r, int L, int R)
{
if(L <= l && R >= r) return mx[p];
if(L > r || R < l) return ;
push_down(p); int mid = (l + r) >> ;
return max(query(p << , l, mid, L, R), query(p << | , mid + , r, L, R));
}
}ST; struct Link_Cut_Tree
{
int fa[maxn], rev[maxn], ch[maxn][];
int sum[maxn], LC[maxn], RC[maxn], col[maxn];
int tot, flag, mark[maxn], top[maxn];
bool is_root(int u) { return (ch[fa[u]][] != u) && ( ch[fa[u]][] != u ); }
void Modify(int u, int x) { if(!u) return; mark[u] = x; LC[u] = RC[u] = col[u] = x, sum[u] = ; }
void Rev(int u) { if(!u) return; rev[u] ^= ; swap(ch[u][], ch[u][]); swap(LC[u], RC[u]); } void push_down(int u)
{
if(!is_root(u)) push_down(fa[u]);
if(rev[u]) Rev(ch[u][]), Rev(ch[u][]), rev[u] = ;
if(mark[u]) Modify(ch[u][], mark[u]), Modify(ch[u][], mark[u]), mark[u] = ;
} void update(int u)
{
int lc = ch[u][], rc = ch[u][];
if(flag)
{
sum[u] = sum[lc] + sum[rc] + ((col[u] != RC[lc]) && (col[u] != LC[rc]));
sum[u] -= ((col[u] == RC[lc]) && (col[u] == LC[rc]));
LC[u] = LC[lc], RC[u] = RC[rc];
if(!lc) LC[u] = col[u]; if(!rc) RC[u] = col[u];
}
else top[u] = top[lc] ? top[lc] : u;
} void rotate(int u)
{
int f = fa[u], gf = fa[f];
int k = ch[f][] == u;
fa[u] = gf; if(!is_root(f)) ch[gf][ch[gf][] == f] = u;
ch[f][k] = ch[u][k ^ ], fa[ch[u][k ^ ]] = f;
fa[f] = u, ch[u][k ^ ] = f;
update(f), update(u);
} void Splay(int u)
{
push_down(u);
while(!is_root(u))
{
int f = fa[u], gf = fa[f];
if(!is_root(f)) (ch[gf][] == f) ^ (ch[f][] == u) ? rotate(u) : rotate(f);
rotate(u);
}
update(u);
} void Access(int u)
{
for(int i = u, last = ; i; last = i, i = fa[i])
{
Splay(i);
if(!flag)
{
int x = top[last], y = top[ch[i][]];
if(x) ST.update(, , n, dfn[x], dfn[x] + size[x] - , -);
if(y) ST.update(, , n, dfn[y], dfn[y] + size[y] - , );
}
ch[i][] = last; update(i);
}
}
void Make_root(int u) { Access(u); Splay(u); Rev(u); }
void Split(int u, int v) { Make_root(u); Access(v); Splay(v); } void Change1(int u) { ++ tot; Access(u); }
void Change2(int u) { Split(, u); Modify(u, ++ tot); }
void Link(int u, int v) { Make_root(u); fa[u] = v; } int Query(int u, int v) { Split(u, v); return sum[v]; }
}LCT1, LCT2; void dfs(int u, int fa)
{
LCT1.fa[u] = fa;
dfn[u] = ++ timer; size[u] = ; dep[u] = dep[fa] + ;
id[timer] = u; LCT1.top[u] = u;
for(int i = E1.head[u]; i; i = E1.last[i])
{
int v = E1.to[i];
if(v == fa) continue;
dfs(v, u); size[u] += size[v];
}
} int main()
{
n = read(), m = read(); LCT2.flag = ;
for(int i = ; i <= n; i ++)
LCT2.col[i] = LCT2.RC[i] = LCT2.LC[i] = ++ LCT2.tot, LCT2.sum[i] = ;
for(int i = ; i < n; i ++)
{
int x = read(), y = read();
E1.add(x, y); LCT2.Link(x, y);
}
dfs(, );
ST.Build(, , n);
for(int i = ; i <= m; i ++)
{
int opt = read();
if(opt == )
{
int x = read();
LCT1.Change1(x); LCT2.Change2(x);
}
else if(opt == )
{
int x = read(), y = read();
printf("%d\n", LCT2.Query(x, y));
}
else if(opt == )
{
int x = read();
printf("%d\n", ST.query(, , n, dfn[x], dfn[x] + size[x] - ));
}
}
return ;
}

【题解】SDOI2017树点涂色的更多相关文章

  1. 【LG3703】[SDOI2017]树点涂色

    [LG3703][SDOI2017]树点涂色 题面 洛谷 题解 更博辣,更博辣!!! 猪年的第一篇博客 一次只能染根到\(x\),且染的颜色未出现过 这句话是我们解题的关键. 设\(x\)到根的颜色数 ...

  2. 【BZOJ4817】[Sdoi2017]树点涂色 LCT+线段树

    [BZOJ4817][Sdoi2017]树点涂色 Description Bob有一棵n个点的有根树,其中1号点是根节点.Bob在每个点上涂了颜色,并且每个点上的颜色不同.定义一条路径的权值是:这条路 ...

  3. [Bzoj4817] [Sdoi2017]树点涂色 (LCT神题)

    4817: [Sdoi2017]树点涂色 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 629  Solved: 371[Submit][Status ...

  4. [Sdoi2017]树点涂色 [lct 线段树]

    [Sdoi2017]树点涂色 题意:一棵有根树,支持x到根染成新颜色,求x到y颜色数,求x子树里点到根颜色数最大值 考场发现这个信息是可减的,但是没想到lct 特意设计成lct的形式! 如何求颜色数? ...

  5. P3703 [SDOI2017]树点涂色

    P3703 [SDOI2017]树点涂色 链接 分析: 首先对于询问,感觉是线段树维护dfs序,每个点记录到根的颜色个数.第二问差分,第三问区间取max. 那么考虑修改,每次将一个点的颜色变成和父节点 ...

  6. [BZOJ4817][SDOI2017]树点涂色(LCT+DFS序线段树)

    4817: [Sdoi2017]树点涂色 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 692  Solved: 408[Submit][Status ...

  7. BZOJ 4817 [SDOI2017]树点涂色 (LCT+线段树维护dfs序)

    题目大意:略 涂色方式明显符合$LCT$里$access$操作的性质,相同颜色的节点在一条深度递增的链上 用$LCT$维护一个树上集合就好 因为它维护了树上集合,所以它别的啥都干不了了 发现树是静态的 ...

  8. BZOJ 4817: [Sdoi2017]树点涂色(LCT+树剖+线段树)

    题目描述 Bob有一棵 nn 个点的有根树,其中1号点是根节点.Bob在每个点上涂了颜色,并且每个点上的颜色不同. 定义一条路径的权值是:这条路径上的点(包括起点和终点)共有多少种不同的颜色. Bob ...

  9. P3703 [SDOI2017]树点涂色 LCT维护颜色+线段树维护dfs序+倍增LCA

    \(\color{#0066ff}{ 题目描述 }\) Bob有一棵\(n\)个点的有根树,其中1号点是根节点.Bob在每个点上涂了颜色,并且每个点上的颜色不同. 定义一条路径的权值是:这条路径上的点 ...

随机推荐

  1. Hough变换-理解篇

    Hough变换-理解篇 霍夫变换(Hough Transform)是图像处理中的一种特征提取技术,它通过一种投票算法检测具有特定形状的物体.该过程在一个参数空间中通过计算累计结果的局部最大值得到一个符 ...

  2. ProtoBuffer由.proto文件生成.cc/.h

    ProtoBuffer由.proto文件生成.cc/.h 一:编译源码下载地址:http://code.google.com/p/protobuf/downloads/list 下载后,根据编译说明进 ...

  3. spark 执行架构

    术语定义 Application:Spark Application的概念和Hadoop MapReduce中的类似,指的是用户编写的Spark应用程序,包含了一个Driver 功能的代码和分布在集群 ...

  4. ReadyAPI 教程和示例(二)

    声明:如果你想转载,请标明本篇博客的链接,请多多尊重原创,谢谢! 本篇使用的 ReadyAPI版本是2.5.0 接上一篇: 4.修改SoapUI测试 本节将演示如何为测试用例添加测试步骤以及更改请求参 ...

  5. Unity Android设备的输入

    Unity Android设备的输入 1依据屏幕位置输入 有的时候也许是为了整个有些风格的干净,减少屏幕上的UI图标,以至于摒弃了虚拟按键这种常用的输入方式.为了替代虚拟按键的输入方式而选择了依据点击 ...

  6. TW实习日记:第17天

    今天又改了一堆bug,比如界面的显示bug,Html字符串的处理.优化了一些交互界面,处理了一个模块数据传输的问题.我发现这些bug真的有一半是粗心导致的,真的真的是,写代码一定要细心细心再细心,不然 ...

  7. bson文件的切分

    描述 最近遇到问题需要将较大的bson文件(MongoDB导出的二进制json文件)按文档(记录)进行切分,网上这方面的资料实在太少,弄了一天多终于达到了基本要求(还不知道有没有BUG) 代码 pac ...

  8. jQuery 对象 与 原生 DOM 对象 相互转换

    区别 jQuery 选择器得到的 jQuery对象 和 原生JS 中的document.getElementById() document.querySelector取得的 DOM对象 是两种不同类型 ...

  9. docker学习2

    今天继续学习docker! 搜索镜像 docker search centos 下载镜像 docker pull name(镜像名字) 查看镜像docker images 字段含义分析: TAG:仓库 ...

  10. svn升级(mac)

    原文链接:http://www.jianshu.com/p/c81712ecccb8 升级前 svn版本1.7.20 升级之后 1.9.2 步骤: 1. 下载最新版svn,链接:http://www. ...