P3703 [SDOI2017]树点涂色 LCT维护颜色+线段树维护dfs序+倍增LCA
\(\color{#0066ff}{ 题目描述 }\)
Bob有一棵\(n\)个点的有根树,其中1号点是根节点。Bob在每个点上涂了颜色,并且每个点上的颜色不同。
定义一条路径的权值是:这条路径上的点(包括起点和终点)共有多少种不同的颜色。
Bob可能会进行这几种操作:
- 1 x
把点\(x\)到根节点的路径上所有的点染上一种没有用过的新颜色。
- 2 x y
求\(x\)到\(y\)的路径的权值。
- 3 x
在以x为根的子树中选择一个点,使得这个点到根节点的路径权值最大,求最大权值。
Bob一共会进行\(m\)次操作
\(\color{#0066ff}{输入格式}\)
第一行两个数\(n,m\)。
接下来\(n-1\)行,每行两个数\(a,b\),表示\(a\)与\(b\)之间有一条边。
接下来\(m\)行,表示操作,格式见题目描述
\(\color{#0066ff}{输出格式}\)
每当出现2,3操作,输出一行。
如果是2操作,输出一个数表示路径的权值
如果是3操作,输出一个数表示权值的最大值
\(\color{#0066ff}{输入样例}\)
5 6
1 2
2 3
3 4
3 5
2 4 5
3 3
1 4
2 4 5
1 5
2 4 5
\(\color{#0066ff}{输出样例}\)
3
4
2
2
\(\color{#0066ff}{数据范围与提示}\)
共10个测试点
测试点1,\(1\leq n,m\leq1000\)
测试点2、3,没有2操作
测试点4、5,没有3操作
测试点6,树的生成方式是,对于i(\(2\leq i \leq n\))i(2≤i≤n),在1到\(i-1\)中随机选一个点作为i的父节点。
测试点7,\(1\leq n,m\leq 50000\)
测试点8,\(1\leq n \leq 50000\)
测试点9,10,无特殊限制
对所有数据,\(1\leq n \leq 10^5\),\(1\leq m \leq 10^5\)
时间限制:1s
空间限制:128MB
\(\color{#0066ff}{ 题解 }\)
震惊!这题能用LCT,蒟蒻刚拿到这题一脸懵逼。。。
怎么用LCT维护颜色呢。。。显然不能每种颜色都一个LCT吧。。(MLE。。)
诶,不能每种颜色一个LCT,每种颜色一个Splay呢,这是可以的吧。。。
正好发现一个东西,每时每刻颜色相同的点一定是一条深度严格递增的链!
于是。。。真的就每个颜色一个Splay了
现在开始考虑操作
第一个操作,显然access就行了。。。
第二个操作, 我们肯定是不能琛出x到y的链的,这样就乱了
那么LCT就维护不了这个东西了。但是没有LCT还要支持两个点的询问。。。
复杂度保证的情况下只有树上差分了吧(\(ans[x]+ans[y]-ans[lca]\))
我们记\(f[x]\)为x到根的答案,那么x到y的答案就是\(f[x]+f[y]-2*f[lca]+1\)
加1是因为LCA被减两次
这个LCA显然只能倍增了。。LCT是动不了的
然后你这\(f\)咋求啊?
初始的时候肯定是深度没错, 实际上它就是x到根的虚边的个数+1
因此在access的时候就能修改
可是,虚实变换的时候,影响的是一棵子树啊, 这是子树修改
于是。。。线段树维护个dfs序就可以完美的解决
然后第三个操作,就是线段树上区间max,就没了。。。
#include<bits/stdc++.h>
#define LL long long
LL in() {
char ch; LL x = 0, f = 1;
while(!isdigit(ch = getchar()))(ch == '-') && (f = -f);
for(x = ch ^ 48; isdigit(ch = getchar()); x = (x << 1) + (x << 3) + (ch ^ 48));
return x * f;
}
const int maxn = 1e5 + 10;
struct node {
node *ch[2], *fa;
node() { ch[0] = ch[1] = fa = NULL; }
bool isr() { return this == fa->ch[1]; }
bool ntr() { return fa && (fa->ch[0] == this || fa->ch[1] == this); }
}pool[maxn];
struct Tree {
Tree *ch[2];
int l, r, max, tag;
Tree(int l = 0, int r = 0, int max = 0, int tag = 0): l(l), r(r), max(max), tag(tag) {}
void trn(int val) { max += val, tag += val; }
void upd() { max = std::max(ch[0]->max, ch[1]->max); }
void dwn() {
if(!tag) return;
ch[0]->trn(tag);
ch[1]->trn(tag);
tag = 0;
}
}Tpool[maxn << 2], *Ttail = Tpool, *root;
struct EDGE {
int to;
EDGE *nxt;
EDGE(int to = 0, EDGE *nxt = NULL): to(to), nxt(nxt) {}
}Epool[maxn << 2], *Etail = Epool;
EDGE *head[maxn];
int f[maxn][26], dfn[maxn], dep[maxn], cnt, n, m, redfn[maxn], siz[maxn];
void rot(node *x) {
node *y = x->fa, *z = y->fa;
bool k = x->isr(); node *w = x->ch[!k];
if(y->ntr()) z->ch[y->isr()] = x;
(x->ch[!k] = y)->ch[k] = w;
(y->fa = x)->fa = z;
if(w) w->fa = y;
}
void splay(node *o) {
while(o->ntr()) {
if(o->fa->ntr()) rot(o->isr() ^ o->fa->isr()? o : o->fa);
rot(o);
}
}
void add(int from, int to) {
head[from] = new(Etail++) EDGE(to, head[from]);
}
void dfs(int x, int fa) {
dep[redfn[dfn[x] = ++cnt] = x] = dep[(pool[x].fa = pool + (f[x][0] = fa)) - pool] + (siz[x] = 1);
for(EDGE *i = head[x]; i; i = i->nxt) {
if(i->to == fa) continue;
dfs(i->to, x);
siz[x] += siz[i->to];
}
}
void build(Tree *&o, int l, int r) {
o = new(Ttail++) Tree(l, r, 0, 0);
if(l == r) return(void)(o->max = dep[redfn[l]]);
int mid = (l + r) >> 1;
build(o->ch[0], l, mid), build(o->ch[1], mid + 1, r);
o->upd();
}
void lazy(Tree *o, int l, int r, int k) {
if(l <= o->l && o->r <= r) return (void)(o->trn(k));
o->dwn();
int mid = (o->l + o->r) >> 1;
if(l <= mid) lazy(o->ch[0], l, r, k);
if(r > mid) lazy(o->ch[1], l, r, k);
o->upd();
}
int query(Tree *o, int l, int r) {
if(l <= o->l && o->r <= r) return o->max;
int max = 0;
o->dwn();
int mid = (o->l + o->r) >> 1;
if(l <= mid) max = std::max(max, query(o->ch[0], l, r));
if(r > mid) max = std::max(max, query(o->ch[1], l, r));
return max;
}
node *findroot(node *x) {
while(x->ch[0]) x = x->ch[0];
return x;
}
void access(node *x) {
node *v;
for(node *y = NULL; x; x = (y = x)->fa) {
splay(x);
if(x->ch[1]) v = findroot(x->ch[1]), lazy(root, dfn[v - pool], dfn[v - pool] + siz[v - pool] - 1, 1);
if((x->ch[1] = y)) v = findroot(y), lazy(root, dfn[v - pool], dfn[v - pool] + siz[v - pool] - 1, -1);
}
}
int LCA(int x, int y) {
if(dep[x] < dep[y]) std::swap(x, y);
for(int i = 17; i >= 0; i--) if(dep[f[x][i]] >= dep[y]) x = f[x][i];
if(x == y) return x;
for(int i = 17; i >= 0; i--) if(f[x][i] != f[y][i]) x = f[x][i], y = f[y][i];
return f[x][0];
}
int main() {
n = in(), m = in();
int p, x, y;
for(int i = 1; i < n; i++) x = in(), y = in(), add(x, y), add(y, x);
dfs(1, 0), build(root, 1, n), pool[1].fa = NULL;
for(int j = 1; j <= 17; j++)
for(int i = 1; i <= n; i++)
f[i][j] = f[f[i][j - 1]][j - 1];
while(m --> 0) {
p = in();
if(p == 1) access(pool + in());
if(p == 2) {
x = in(), y = in();
int lca = LCA(x, y);
printf("%d\n", query(root, dfn[x], dfn[x]) + query(root, dfn[y], dfn[y]) - 2 * query(root, dfn[lca], dfn[lca]) + 1);
}
if(p == 3) x = in(), printf("%d\n", query(root, dfn[x], dfn[x] + siz[x] - 1));
}
return 0;
}
P3703 [SDOI2017]树点涂色 LCT维护颜色+线段树维护dfs序+倍增LCA的更多相关文章
- [BZOJ4817][SDOI2017]树点涂色:Link-Cut Tree+线段树
分析 与[BZOJ3779]重组病毒唯一的区别是多了一个链上求实链段数的操作. 因为每条实链的颜色必然不相同且一条实链上不会有两个深度相同的点(好像算法的正确性和第二个条件没什么关系,算了算了),画图 ...
- [Sdoi2017]树点涂色 [lct 线段树]
[Sdoi2017]树点涂色 题意:一棵有根树,支持x到根染成新颜色,求x到y颜色数,求x子树里点到根颜色数最大值 考场发现这个信息是可减的,但是没想到lct 特意设计成lct的形式! 如何求颜色数? ...
- [BZOJ4817][SDOI2017]树点涂色(LCT+DFS序线段树)
4817: [Sdoi2017]树点涂色 Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 692 Solved: 408[Submit][Status ...
- 【BZOJ4817】【SDOI2017】树点涂色 [LCT][线段树]
树点涂色 Time Limit: 10 Sec Memory Limit: 128 MB[Submit][Status][Discuss] Description Bob有一棵n个点的有根树,其中1 ...
- 【BZOJ4817】[Sdoi2017]树点涂色 LCT+线段树
[BZOJ4817][Sdoi2017]树点涂色 Description Bob有一棵n个点的有根树,其中1号点是根节点.Bob在每个点上涂了颜色,并且每个点上的颜色不同.定义一条路径的权值是:这条路 ...
- BZOJ 4817 [SDOI2017]树点涂色 (LCT+线段树维护dfs序)
题目大意:略 涂色方式明显符合$LCT$里$access$操作的性质,相同颜色的节点在一条深度递增的链上 用$LCT$维护一个树上集合就好 因为它维护了树上集合,所以它别的啥都干不了了 发现树是静态的 ...
- [SDOI2017][bzoj4817] 树点涂色 [LCT+线段树]
题面 传送门 思路 $LCT$ 我们发现,这个1操作,好像非常像$LCT$里面的$Access$啊~ 那么我们尝试把$Access$操作魔改成本题中的涂色 我们令$LCT$中的每一个$splay$链代 ...
- BZOJ4817[Sdoi2017]树点涂色——LCT+线段树
题目描述 Bob有一棵n个点的有根树,其中1号点是根节点.Bob在每个点上涂了颜色,并且每个点上的颜色不同.定义一条路 径的权值是:这条路径上的点(包括起点和终点)共有多少种不同的颜色.Bob可能会进 ...
- SDOI2017 树点涂色——LCT the END
Description Bob有一棵n个点的有根树,其中1号点是根节点.Bob在每个点上涂了颜色,并且每个点上的颜色不同.定义一条路 径的权值是:这条路径上的点(包括起点和终点)共有多少种不同的颜色. ...
随机推荐
- SQL 由人员汇总到部门树递归合计总数函数
1.由人员计算出总数,在部门树(tree)按结构汇总(主父绑定) CREATE function [dbo].[GetEmpDepNum] ( @ID int ) RETURNS @Tree Tabl ...
- Spring学习十一
一: 创建bean的方法: 1: 如果不采用构造注入:默认调用bean的无参构造函数,因此该类必须要提供无参构造函数,用无参构造函数用反射创建bean. : 如果采用构造 ...
- SpringMVC---依赖注入与面向切面
1.依赖注入与面向切面 1.1.出现背景 ——如何简化java开发? 其中很重要的一点是“组件化”. ——如何更好的“组件化”? 松耦合,以及尽可能的让组件专注于本身. ——Spring框架的目的也只 ...
- 三种web性能压力测试工具
三种web性能压力测试工具http_load webbench ab小结 题记:压力和性能测试工具很多,下文讨论的是我觉得比较容易上手,用的比较多的三种 http_load 下载地址:http://w ...
- HeapCreate深入研究
本机:win7(x86),4G内存 #include"stdafx.h"#include<windows.h>#include<stdio.h>#inclu ...
- mongodb与mongodb可视化工具adminMongo结合使用
一,MongoDB的安置及配置 1,从MongoDB官网下载安装 https://www.mongodb.com/download-center#community 根据的电脑选择合适的版本安装: 根 ...
- HDLM命令dlnkmgr详解之二__help/clear
1.help操作 主要显示命令的帮助信息. 显示所有操作的帮助信息 -bash-3.2# dlnkmgr help dlnkmgr: Format dlnkmgr { clear | help | o ...
- python爬虫(1)--Urllib库的基本使用
这里使用python2.7,pycharm进行代码编写 1.爬一个静态网页示例 import urllib2 response = urllib2.urlopen("http://www.b ...
- 昨天的笔试题, StringBuffer
代码: public static void switchStr(StringBuffer x, StringBuffer y) { x.append(y); System.out.println(& ...
- 重命名File
File completeFile = new File(mFilePath + mFileName); if (completeFile.exists()) { File fileWithSuffi ...