BZOJ 4817: [Sdoi2017]树点涂色(LCT+树剖+线段树)
题目描述
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操作,输出一个数表示权值的最大值
输入输出样例
说明
共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+树剖+线段树)的更多相关文章
- [BZOJ4817][SDOI2017]树点涂色(LCT+DFS序线段树)
4817: [Sdoi2017]树点涂色 Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 692 Solved: 408[Submit][Status ...
- BZOJ.4817.[SDOI2017]树点涂色(LCT DFS序 线段树)
题目链接 操作\(1.2\)裸树剖,但是操作\(3\)每个点的答案\(val\)很不好维护.. 如果我们把同种颜色的点划分到同一连通块中,那么向根染色的过程就是Access()! 最初所有点间都是虚边 ...
- P3703 [SDOI2017]树点涂色 LCT维护颜色+线段树维护dfs序+倍增LCA
\(\color{#0066ff}{ 题目描述 }\) Bob有一棵\(n\)个点的有根树,其中1号点是根节点.Bob在每个点上涂了颜色,并且每个点上的颜色不同. 定义一条路径的权值是:这条路径上的点 ...
- bzoj4817/luogu3703 树点涂色 (LCT+dfs序+线段树)
我们发现,这个染色的操作他就很像LCT中access的操作(为什么??),然后就自然而然地想到,其实一个某条路径上的颜色数量,就是我们做一个只有access操作的LCT,这条路径经过的splay的数量 ...
- [BZOJ4817][SDOI2017]树点涂色:Link-Cut Tree+线段树
分析 与[BZOJ3779]重组病毒唯一的区别是多了一个链上求实链段数的操作. 因为每条实链的颜色必然不相同且一条实链上不会有两个深度相同的点(好像算法的正确性和第二个条件没什么关系,算了算了),画图 ...
- [Sdoi2017]树点涂色 [lct 线段树]
[Sdoi2017]树点涂色 题意:一棵有根树,支持x到根染成新颜色,求x到y颜色数,求x子树里点到根颜色数最大值 考场发现这个信息是可减的,但是没想到lct 特意设计成lct的形式! 如何求颜色数? ...
- 【BZOJ4817】[Sdoi2017]树点涂色 LCT+线段树
[BZOJ4817][Sdoi2017]树点涂色 Description Bob有一棵n个点的有根树,其中1号点是根节点.Bob在每个点上涂了颜色,并且每个点上的颜色不同.定义一条路径的权值是:这条路 ...
- 【BZOJ4817】【SDOI2017】树点涂色 [LCT][线段树]
树点涂色 Time Limit: 10 Sec Memory Limit: 128 MB[Submit][Status][Discuss] Description Bob有一棵n个点的有根树,其中1 ...
- BZOJ 4817 [SDOI2017]树点涂色 (LCT+线段树维护dfs序)
题目大意:略 涂色方式明显符合$LCT$里$access$操作的性质,相同颜色的节点在一条深度递增的链上 用$LCT$维护一个树上集合就好 因为它维护了树上集合,所以它别的啥都干不了了 发现树是静态的 ...
- BZOJ 4817: [Sdoi2017]树点涂色 LCT+Access的性质+DFS序+线段树
Code: #include<bits/stdc++.h> #define maxn 200003 #define inf -1000000 using namespace std; vo ...
随机推荐
- 高性能Web服务器Nginx的配置与部署研究(14)平滑升级你的Nginx
1.概述(可以直接跳过看第2部分) Nginx方便地帮助我们实现了平滑升级.其原理简单概括,就是: (1)在不停掉老进程的情况下,启动新进程. (2)老进程负责处理仍然没有处理完的请求,但不再接受处理 ...
- Eclipse设置Tab键缩进4个空格的步骤,也就是按一下Tab键输出四个空格
Eclipse设置Tab键缩进4个空格的步骤,也就是按1下Tab键输出4个空格,步奏如下 1.点击 window->preference-,选择 General->Editors-> ...
- Jenkins + Jmeter +Ant自动化集成环境搭建(一)
所需工具 一.jmeter 工具下载 https://jmeter.apache.org/ 配置环境JDK等及各种插件可以看小七之前的教程 二.Ant安装(http://ant.apache.org ...
- leetcode BFS解题思路
Word Ladder 思路一:单向bfs, 使用visited数组记录哪些已经访问过了, 访问过的就不允许再次入队, 同时这里想到的是使用26个英文字母,枚举可能的取值, 类似brute force ...
- 面向对象的JavaScript-008-Function介绍
1. // 函数 /* Declare the function 'myFunc' */ function myFunc(theObject) { theObject.brand = "To ...
- Linux ssldump命令
一.简介 tcpdump是一款很强大.很有用的网络侦听软件,但是对于ssl加密的数据包就无能为力了:ssldump则是一款可以侦听ssl加密的数据包的软件. 二.安装 1)通过yum安装 yum ...
- python 多继承详解-乾颐堂
1 2 3 4 5 6 7 8 9 10 class A(object): # A must be new-style class def __init__(self): prin ...
- smarty 使用php函数
strtotime() time() <{if strtotime($activity.start_time) gt time()}> <a href="?action=d ...
- [ASP.NET MVC 小牛之路]03 - Razor语法(转)
出处:http://www.cnblogs.com/willick/p/3224144.html Razor是MVC3中才有的新的视图引擎.我们知道,在ASP.NET中,ASPX的视图引擎依靠< ...
- 视频分析(MATLAB)——MV分镜头图像分类
引言:一个MV视频是有很多帧图像组合而成的,而一支MV是有多少个分镜头场景组合而成的呢?由MATLAB如何自动实现? 以<Love You Like A Love Song>的MV为例(这 ...