题目描述

Bob有一棵 nn 个点的有根树,其中1号点是根节点。Bob在每个点上涂了颜色,并且每个点上的颜色不同。

定义一条路径的权值是:这条路径上的点(包括起点和终点)共有多少种不同的颜色。

Bob可能会进行这几种操作:

  • 1 x

把点 xx 到根节点的路径上所有的点染上一种没有用过的新颜色。

  • 2 x y

求 xx 到 yy 的路径的权值。

  • 3 x

在以x为根的子树中选择一个点,使得这个点到根节点的路径权值最大,求最大权值。

Bob一共会进行 mm 次操作

输入输出格式

输入格式:

第一行两个数 n,mn,m 。

接下来 n-1n−1 行,每行两个数 a,ba,b ,表示 aa 与 bb 之间有一条边。

接下来 mm 行,表示操作,格式见题目描述

输出格式:

每当出现2,3操作,输出一行。

如果是2操作,输出一个数表示路径的权值

如果是3操作,输出一个数表示权值的最大值

输入输出样例

输入样例#1: 复制

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
输出样例#1: 复制

3
4
2
2

说明

共10个测试点

测试点1, 1\leq n,m\leq10001≤n,m≤1000

测试点2、3,没有2操作

测试点4、5,没有3操作

测试点6,树的生成方式是,对于 i(2\leq i \leq n)i(2≤i≤n) ,在1到 i-1i−1 中随机选一个点作为i的父节点。

测试点7, 1\leq n,m\leq 500001≤n,m≤50000

测试点8, 1\leq n \leq 500001≤n≤50000

测试点9,10,无特殊限制

对所有数据, 1\leq n \leq 10^51≤n≤105 , 1\leq m \leq 10^51≤m≤105

时间限制:1s

空间限制:128MB

题解

照例先膜一发大佬

说真的是道好题,LCT,树剖,dfs序,线段树都得用上

然后抄题解抄得不亦乐乎悲催的没有发现一个地方我和大佬思路是完全不一样的……

后来因为这个地方调了整整一个小时……我少了一个小时的睡觉时间!!!

先讲个思路……

操作一

因为颜色都不一样,也没有必要维护颜色了

然后我们能发现,任何时候同一个颜色必定是一条链,而且深度严格递增

很显然,因为每一次颜色修改只会在到根的路径上进行

所以每一个颜色都可以用LCT中的splay来维护了

并且我们发现,操作一不就是LCT中的access么?

操作二

每一个颜色都在一个splay中

所以一条路径上的颜色数量就是跨过了几个splay,也就是经过了几条虚边

于是split就好了显然是行不通的,因为因为乱搞会破坏掉关系

然后可以考虑用树上差分

$f[x]+f[y]-2*f[lca(x,y)]+1$就是这条路径上的颜色数量了(lca被减了两次要加回来)(f表示到根节点的虚边数量)

然后考虑怎么维护$f$

刚开始时是节点的深度

然后显然access的时候会对$f$有影响,一条边变虚,一条边变实

所以只要把原来的虚边指向的子树答案全部+1,实边指向的子树答案全部-1

这就是一个子树操作啦

用线段树+dfs序,可以很轻松的完成子树操作

操作三

查询其实和修改差不多

直接线段树查询即可

ps:写的树剖求LCA,结果因为原树和splay都要维护father,看了看大佬的板子只用了一个数组,结果莫名其妙T到死……算了滚去睡觉了(¦3[▓▓]实在太困了……

 //minamoto
#include<iostream>
#include<cstdio>
using std::swap;
using std::max;
#define getc() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
char buf[<<],*p1=buf,*p2=buf;
inline int read(){
#define num ch-'0'
char ch;bool flag=;int res;
while(!isdigit(ch=getc()))
(ch=='-')&&(flag=true);
for(res=num;isdigit(ch=getc());res=res*+num);
(flag)&&(res=-res);
#undef num
return res;
}
char obuf[<<],*o=obuf;
inline void print(int x){
if(x>) print(x/);
*o++=x%+;
}
const int N=,M=N*;
int fa[N],f[N],ch[N][],sz[N],son[N],dfn[N],rk[N];
int dep[N],top[N],L[M],R[M],mx[M],lz[M],rs[N],mid[M];
int head[N],Next[N<<],ver[N<<];
int n,m,cnt,num,tot;
inline void add(int u,int v){
ver[++tot]=v,Next[tot]=head[u],head[u]=tot;
ver[++tot]=u,Next[tot]=head[v],head[v]=tot;
}
void dfs1(int u){
sz[u]=,dep[u]=dep[f[u]]+;
dfn[u]=++num,rk[num]=u;
for(int i=head[u];i;i=Next[i]){
int v=ver[i];
if(v==f[u]) continue;
fa[v]=f[v]=u;
dfs1(v);
sz[u]+=sz[v];
if(!son[u]||sz[v]>sz[son[u]]) son[u]=v;
}
rs[u]=num;
}
void dfs2(int u){
if(!top[u]) top[u]=u;
if(!son[u]) return;
top[son[u]]=top[u],dfs2(son[u]);
for(int i=head[u];i;i=Next[i]){
int v=ver[i];
if(v!=f[u]&&v!=son[u]) dfs2(v);
}
}
inline int LCA(int u,int v){
while(top[u]!=top[v])
dep[top[u]]>dep[top[v]]?u=f[top[u]]:v=f[top[v]];
return dep[u]<dep[v]?u:v;
}
inline void build(int p,int l,int r){
L[p]=l,R[p]=r,mid[p]=(l+r)>>;
if(l==r)return (void)(mx[p]=dep[rk[l]]);
build(p<<,l,mid[p]),build(p<<|,mid[p]+,r);
mx[p]=max(mx[p<<],mx[p<<|]);
}
#define pushdown if(lz[p]) update(p<<1,L[p],mid[p],lz[p]),update(p<<1|1,mid[p]+1,R[p],lz[p]),lz[p]=0
void update(int p,int l,int r,int v){
if(L[p]==l&&R[p]==r){mx[p]+=v,lz[p]+=v;return;}
pushdown;
if(r<=mid[p]) update(p<<,l,r,v);
else if(l>mid[p]) update(p<<|,l,r,v);
else update(p<<,l,mid[p],v),update(p<<|,mid[p]+,r,v);
mx[p]=max(mx[p<<],mx[p<<|]);
}
int get(int v){
int p=;
while(L[p]!=R[p]){
pushdown;p=(p<<)+(v>mid[p]);
/*pushdown后面逗号竟然死循环了……*/
}
return mx[p];
}
int ask(int p,int l,int r){
if(L[p]==l&&R[p]==r) return mx[p];
pushdown;
if(r<=mid[p]) return ask(p<<,l,r);
if(l>mid[p]) return ask(p<<|,l,r);
return max(ask(p<<,l,mid[p]),ask(p<<|,mid[p]+,r));
}
#undef pushdown
bool isroot(int x){
return ch[fa[x]][]!=x&&ch[fa[x]][]!=x;
}
void rotate(int x){
int y=fa[x],z=fa[y],d=ch[y][]==x;
if(!isroot(y)) ch[z][ch[z][]==y]=x;
fa[x]=z,fa[y]=x,fa[ch[x][d^]]=y,ch[y][d]=ch[x][d^],ch[x][d^]=y;
}
void splay(int x){
for(int y=fa[x];!isroot(x);y=fa[x]){
if(!isroot(y))
rotate(x);
rotate(x);
}
}
int findroot(int x){
while(ch[x][]) x=ch[x][];
return x;
}
void access(int x){
for(int w,y=;x;x=fa[y=x]){
splay(x);
if(ch[x][]) w=findroot(ch[x][]),update(,dfn[w],rs[w],);
if((ch[x][]=y)) w=findroot(y),update(,dfn[w],rs[w],-);
/*把原来的子树变为虚的,整个子树的答案+1
然后新的子树答案-1*/
}
}
int main(){
//freopen("testdata.in","r",stdin);
int n=read(),m=read();
for(int i=;i<n;++i){
int u=read(),v=read();
add(u,v);
}
dfs1(),dfs2(),build(,,n);
while(m--){
int opt=read(),x=read();
switch(opt){
case :{
access(x);
break;
}
case :{
int y=read();
print(get(dfn[x])+get(dfn[y])-get(dfn[LCA(x,y)])*+),*o++='\n';
break;
}
case :{
print(ask(,dfn[x],rs[x])),*o++='\n';
break;
}
}
}
fwrite(obuf,o-obuf,,stdout);
return ;
}

BZOJ 4817: [Sdoi2017]树点涂色(LCT+树剖+线段树)的更多相关文章

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

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

  2. BZOJ.4817.[SDOI2017]树点涂色(LCT DFS序 线段树)

    题目链接 操作\(1.2\)裸树剖,但是操作\(3\)每个点的答案\(val\)很不好维护.. 如果我们把同种颜色的点划分到同一连通块中,那么向根染色的过程就是Access()! 最初所有点间都是虚边 ...

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

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

  4. bzoj4817/luogu3703 树点涂色 (LCT+dfs序+线段树)

    我们发现,这个染色的操作他就很像LCT中access的操作(为什么??),然后就自然而然地想到,其实一个某条路径上的颜色数量,就是我们做一个只有access操作的LCT,这条路径经过的splay的数量 ...

  5. [BZOJ4817][SDOI2017]树点涂色:Link-Cut Tree+线段树

    分析 与[BZOJ3779]重组病毒唯一的区别是多了一个链上求实链段数的操作. 因为每条实链的颜色必然不相同且一条实链上不会有两个深度相同的点(好像算法的正确性和第二个条件没什么关系,算了算了),画图 ...

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

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

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

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

  8. 【BZOJ4817】【SDOI2017】树点涂色 [LCT][线段树]

    树点涂色 Time Limit: 10 Sec  Memory Limit: 128 MB[Submit][Status][Discuss] Description Bob有一棵n个点的有根树,其中1 ...

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

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

  10. BZOJ 4817: [Sdoi2017]树点涂色 LCT+Access的性质+DFS序+线段树

    Code: #include<bits/stdc++.h> #define maxn 200003 #define inf -1000000 using namespace std; vo ...

随机推荐

  1. 遍历List集合时,删除数据的问题

    一.问题描述 有时候,我们会遇到在遍历List集合的过程中删除数据的情况. 看着自己写的代码,感觉完全没有问题,但就是达不到预期的效果,这是为什么呢?下面我们来分析下 String str1 = ne ...

  2. 什么是响应式编程——响应式Spring的道法术器

    响应式编程之道 1.1 什么是响应式编程? 在开始讨论响应式编程(Reactive Programming)之前,先来看一个我们经常使用的一款堪称“响应式典范”的强大的生产力工具——电子表格. 举个简 ...

  3. python 字符编码处理问题总结

    Python中常常遇到这种字符编码问题,尤其在处理网页源代码时(特别是爬虫中): UnicodeDecodeError: ‘XXX' codec can't decode bytes in posit ...

  4. ubuntu14.04 64位安装 g2o

    参考链接:http://blog.csdn.net/jiujiu932/article/details/52248577 http://www.cnblogs.com/gaoxiang12/p/473 ...

  5. Sqlserver时间函数用法(二)

    --1. 当前系统日期.时间 select getdate() --2015-01-06 09:27:27.277 --2.时间操作 dateadd 在向指定日期加上一段时间的基础上,返回新的 dat ...

  6. 1710 生日蛋糕(1999 noi)

    1710 生日蛋糕(1999 noi) 1999年NOI全国竞赛 题目描述 Description 7月17日是Mr.W的生日,ACM-THU为此要制作一个体积为Nπ的M层生日蛋糕,每层都是一个圆柱体 ...

  7. Oracle 递归

      当对象存在父节点.子节点时,通过特定的方式获取父节点.子节点数据构建树状结构或其它形式结构时,通常都会使用递归,如:一个公司有多个部门.每个部门下可能有多个小部门,小部门下面又有组-.为了数据容易 ...

  8. 假设字符串类似这样的aba和aab,abc和bca就相等,现在随便给你二组字符串,请编程比较他们看是否相等

    public static boolean stringSame(String str1,String str2){ if(str1.length() != str2.length()){//先判断长 ...

  9. osgQt支持触摸屏

    1. osgQt的构造函数添加:setAttribute(Qt::WA_AcceptTouchEvents);//wyh 2. event()修改,支持触摸时间 bool GLWidget::even ...

  10. C#中继承和构造函数

    一个类继承自另外一个类,他们的构造函数改怎么办? 首先必须先声明:构造函数是不能继承的 我们先看一段代码:第一段代码没有构造函数,第二段有一个,第三段有两个.从他们的MSIL可以看出,有几个构造函数就 ...