[SDOI2017][bzoj4817] 树点涂色 [LCT+线段树]
题面
思路
$LCT$
我们发现,这个1操作,好像非常像$LCT$里面的$Access$啊~
那么我们尝试把$Access$操作魔改成本题中的涂色
我们令$LCT$中的每一个$splay$链代表同一种颜色的一条链,那么$Access(u)$就相当于把这一段变成同一种颜色
注意这个东西能成立,是因为每次涂上的都是新的一种颜色(所以如果有$m$种颜色,每次涂其中一种,可能重复的之类的就不能这么做了)
线段树
接下来我们解决询问的问题:什么结构能维护链上信息和子树信息(同时)?当然是线段树了~
我们考虑开一棵以$dfs$序为下标的线段树,线段树的每个叶节点保存这个节点到根的路径权值,其他节点维护对应区间的最大值
这样,询问三就变成了区间求$max$,询问二则可以化成$w[u]+w[v]-2*w[lca]+1$这样的形式($w[u]$表示$u$到根路径上的权值)(这个东西不管颜色怎么分布,一定是对的,证明很容易,可以自己想想)
但是这样之后,我们在修改一的时候,怎么修改线段树上的值呢?
维护线段树
我们发现,在修改的过程中,每一次我们从当前$splay$连向上面的$splay$时,会把一条虚边变成重边、一条重边变成虚边
那么,原来重边下的这棵子树,因为它上面的东西变成了新的颜色,而上面的东西本来是和它一个颜色的,所以重边的这棵子树中所有节点的权值要+1
而对于原来虚边下面的这棵子树来说,它上面的东西本来和它不是一个颜色的,现在是同一个颜色了,所以虚边的子树中所有节点的权值要-1
这样,我们只要在$Access$的时候,同时维护线段树的权值就可以了
总结
本题从与$Access$相似的修改方式开始,切入点是$LCT$,然后加入了线段树维护,并且确定了在$Access$操作的同时修改线段树的值
总时间复杂度为均摊$O(nlog^2n)$
Code
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cassert>
#include<cmath>
#define ll long long
using namespace std;
inline int read(){
int re=0,flag=1;char ch=getchar();
while(ch>'9'||ch<'0'){
if(ch=='-') flag=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9') re=(re<<1)+(re<<3)+ch-'0',ch=getchar();
return re*flag;
}
int n,Q,first[100010],cnte,dep[100010],st[100010][20],dfn[100010],lim[100010],back[100010],cntn;
int fa[100010];
struct edge{
int to,next;
}e[200010];
inline void add(int u,int v){
e[++cnte]=(edge){v,first[u]};first[u]=cnte;
e[++cnte]=(edge){u,first[v]};first[v]=cnte;
}
void dfs(int u,int f){
int i,v;dep[u]=dep[f]+1;st[u][0]=f;fa[u]=f;//这里把LCT的fa数组预处理好(此时没有边,都是虚边)
dfn[u]=++cntn;back[cntn]=u;
for(i=first[u];~i;i=e[i].next){
v=e[i].to;if(v==f) continue;
dfs(v,u);
}
lim[u]=cntn;
}
void ST(){
for(int j=1;j<=19;j++){
for(int i=1;i<=n;i++) st[i][j]=st[st[i][j-1]][j-1];
}
}
int lca(int l,int r){
int i;
if(dep[l]>dep[r]) swap(l,r);
for(i=19;i>=0;i--) if(dep[st[r][i]]>=dep[l]) r=st[r][i];
if(l==r) return l;
for(i=19;i>=0;i--){
if(st[r][i]!=st[l][i]){
l=st[l][i];
r=st[r][i];
}
}
return st[l][0];
}
//segment tree
int a[400010],lazy[400010];
void update(int num){
a[num]=max(a[num<<1],a[num<<1|1]);
}
void push(int l,int r,int num){
if(l==r||!lazy[num]) return;
a[num<<1]+=lazy[num];a[num<<1|1]+=lazy[num];
lazy[num<<1]+=lazy[num];lazy[num<<1|1]+=lazy[num];
lazy[num]=0;
}
void build(int l,int r,int num){
if(l==r){a[num]=dep[back[l]];return;}
int mid=(l+r)>>1;
build(l,mid,num<<1);build(mid+1,r,num<<1|1);
update(num);
}
void change(int l,int r,int ql,int qr,int num,int ch){
push(l,r,num);
if(l>=ql&&r<=qr){a[num]+=ch;lazy[num]+=ch;return;}
int mid=(l+r)>>1;
if(mid>=ql) change(l,mid,ql,qr,num<<1,ch);
if(mid<qr) change(mid+1,r,ql,qr,num<<1|1,ch);
update(num);
}
int query(int l,int r,int ql,int qr,int num){
push(l,r,num);
if(l>=ql&&r<=qr) return a[num];
int mid=(l+r)>>1,re=-1e9;
if(mid>=ql) re=max(re,query(l,mid,ql,qr,num<<1));
if(mid<qr) re=max(re,query(mid+1,r,ql,qr,num<<1|1));
return re;
}
//link cut tree
int ch[100010][2]={0},rt[100010];
int get(int pos){return ch[fa[pos]][1]==pos;}
void rotate(int x){
int f=fa[x],ff=fa[f],son=get(x);
ch[f][son]=ch[x][son^1];
if(ch[f][son]) fa[ch[f][son]]=f;
fa[f]=x;ch[x][son^1]=f;
fa[x]=ff;
if(rt[f]) rt[x]=1,rt[f]=0;
else ch[ff][ch[ff][1]==f]=x;
}
void splay(int pos){
if(rt[pos]) return;
for(int f;!rt[pos];rotate(pos)){
if(!rt[f=fa[pos]])
rotate((get(f)==get(pos))?f:pos);
}
}
int pre(int pos){
while(ch[pos][0]) pos=ch[pos][0];
return pos;
}
void access(int pos){
for(int tmp=0,tt;pos;tmp=pos,pos=fa[pos]){
splay(pos);
if(ch[pos][1]){//重边变虚边,+1
tt=pre(ch[pos][1]);
change(1,n,dfn[tt],lim[tt],1,1);
}
rt[ch[pos][1]]=1;ch[pos][1]=tmp;rt[tmp]=0;
if(tmp){//虚边变重边,-1
tt=pre(tmp);
change(1,n,dfn[tt],lim[tt],1,-1);
}
}
}
int main(){
memset(first,-1,sizeof(first));
n=read();Q=read();int i,t1,t2,t3,f;
for(i=1;i<n;i++){
t1=read();t2=read();
add(t1,t2);
}
dfs(1,0);ST();build(1,n,1);
for(i=1;i<=n;i++) rt[i]=1;
while(Q--){
t1=read();
if(t1==1){
t2=read();access(t2);
}
if(t1==2){
t2=read();t3=read();
f=lca(t2,t3);
printf("%d\n",query(1,n,dfn[t2],dfn[t2],1)+query(1,n,dfn[t3],dfn[t3],1)-2*query(1,n,dfn[f],dfn[f],1)+1);
}
if(t1==3){
t2=read();
printf("%d\n",query(1,n,dfn[t2],lim[t2],1));
}
}
}
[SDOI2017][bzoj4817] 树点涂色 [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 ...
- [Sdoi2017]树点涂色 [lct 线段树]
[Sdoi2017]树点涂色 题意:一棵有根树,支持x到根染成新颜色,求x到y颜色数,求x子树里点到根颜色数最大值 考场发现这个信息是可减的,但是没想到lct 特意设计成lct的形式! 如何求颜色数? ...
- BZOJ4817[Sdoi2017]树点涂色——LCT+线段树
题目描述 Bob有一棵n个点的有根树,其中1号点是根节点.Bob在每个点上涂了颜色,并且每个点上的颜色不同.定义一条路 径的权值是:这条路径上的点(包括起点和终点)共有多少种不同的颜色.Bob可能会进 ...
- bzoj4817 & loj2001 [Sdoi2017]树点涂色 LCT + 线段树
题目传送门 https://lydsy.com/JudgeOnline/problem.php?id=4817 https://loj.ac/problem/2001 题解 可以发现这个题就是 bzo ...
- BZOJ 4817 [SDOI2017]树点涂色 (LCT+线段树维护dfs序)
题目大意:略 涂色方式明显符合$LCT$里$access$操作的性质,相同颜色的节点在一条深度递增的链上 用$LCT$维护一个树上集合就好 因为它维护了树上集合,所以它别的啥都干不了了 发现树是静态的 ...
- 【bzoj4817】树点涂色 LCT+线段树+dfs序
Description Bob有一棵n个点的有根树,其中1号点是根节点.Bob在每个点上涂了颜色,并且每个点上的颜色不同.定义一条路 径的权值是:这条路径上的点(包括起点和终点)共有多少种不同的颜色. ...
- BZOJ 4817 [Sdoi2017]树点涂色 ——LCT 线段树
同BZOJ3779. SDOI出原题,还是弱化版的. 吃枣药丸 #include <map> #include <cmath> #include <queue> # ...
- BZOJ 4817: [Sdoi2017]树点涂色(lct+线段树)
传送门 解题思路 跟重组病毒这道题很像.只是有了一个询问\(2\)的操作,然后询问\(2\)的答案其实就是\(val[x]+val[y]-2*val[lca(x,y)]+1\)(画图理解).剩下的操作 ...
随机推荐
- 关于 Angular引用Material出现node_modules/@angular/material/button-toggle/typings/button-toggle.d.ts(154,104): error TS2315: Type 'ElementRef' is not generic.问题
百度了好久 ,,,最后谷歌出来了.. 该错误可能来自于您将@ angular / material设置为6.0.0, 但所有其他Angular包都是5.x.您应该始终确保Material主要版本与An ...
- 第34-1题:LeetCode112. Path Sum I
题目 给定一个二叉树和一个目标和,判断该树中是否存在根节点到叶子节点的路径,这条路径上所有节点值相加等于目标和. 说明: 叶子节点是指没有子节点的节点. 示例: 给定如下二叉树,以及目标和 sum ...
- lintcode_69_二叉树的层次遍历
二叉树的层次遍历 描述 笔记 数据 评测 给出一棵二叉树,返回其节点值的层次遍历(逐层从左往右访问) 您在真实的面试中是否遇到过这个题? Yes 哪家公司问你的这个题? LinkedIn Airb ...
- 爬虫学习(十三)——xpath基础学习
lxml的作用 lxml是HTML.xml的解析器,主要的功能是如何解析和提取HTML和xml数据 lxml和正则一样,也是使用C来实现的,是一款高性能的python HTML/xml解析器,我们可以 ...
- Docker自学纪实(五) 使用Dockerfile构建php网站环境镜像
一般呢,docker构建镜像容器的方式有两种:一种是pull dockerhub仓库里面的镜像,一种是使用Dockerfile自定义构建镜像. 很多时候,公司要求的镜像并不一定符合dockerhub仓 ...
- JZOJ 5913. 林下风气
Description 里口福因有林下风气,带领全国各地高校掀起了一股AK风,大家都十分痴迷于AK.里口福为了打击大家的自信心,出了一道自以为十分困难的题目.里口福有一棵树,第i个节点上有点权ai,他 ...
- 【Hadoop/Hive/mapreduce】系列之如何删除HIVE 表格的分区
今天的一个业务场景就是要把三年的数据从第一天不停的融合起来,每一天作为表格一个新的分区.由于空间有限,数据量很大,可能每天数据都是几十个G的大小.所以我需要做的一点就是在融合这一天之后,删除一天的分区 ...
- perl语言入门总结-第5章-输入输出
读取标准输入 chomp($line = <STDIN>); #去掉后面的换行 while (defined($line = <STDIN>)) { print "I ...
- Python 装饰器执行顺序迷思
Table of Contents 1. 探究多个装饰器执行顺序 1.1. 疑问 1.2. 函数和函数调用的区别 1.3. 装饰器函数在被装饰函数定义好后立即执行 1.4. 疑问的解释 2. 参考资料 ...
- 2139: road
把a[i], b[i]分开来排序 对应位置上的点连边 感性理解这是最小的 会连出若干个环 要使得若干个环连成大环 令a[i]向b[i - 1] 连边 易证一定能使图联通 感性理解这也是最小的 #inc ...