[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\)(画图理解).剩下的操作 ...
随机推荐
- C#+Winform开发窗体程序
学习笔记 第一章:winform基础 一.概述 1.Windows Form(简称WinForm) 是微软.NET平台下用于开发"图形界面"应用程序的组件. 2.C/S架构 客户机 ...
- C#基础学习笔记(个人整理)
学习笔记 第一章:c#基础 一.程序设计语言的发展及历史 1.程序设计语言? 通俗也叫编程语言,实现人与机器交互的工具 2.历史 1)机器语言 : 0,1 2)汇编语言 : 包含一些机器语言,同时增加 ...
- ssm整合-错误3
1.警告: Unknown version string [3.1]. Default version will be used. 因为Tomcat版本为7,支持3.1版本的为Tomcat 8: 2. ...
- BeanFactory和IOC控制反转
之前在看spring,看IOC实在是云里雾里,包括看AOP也是云里雾里的,后来重新学习Java Web,做了一个简单的web项目,再之后看了崔希凡老师的视频,Day27和Day28两天的内容,真的很有 ...
- python文件操作练习之文件备份
文件备份 ## 文件备份 # 打开文件 def backup(file1, file2): with open(file1, 'rb') as f1,\ open(file2, 'wb') as f2 ...
- P1282
题目描述 多米诺骨牌有上下2个方块组成,每个方块中有1~6个点.现有排成行的 上方块中点数之和记为S1,下方块中点数之和记为S2,它们的差为|S1-S2|.例如在图8-1中,S1=6+1+1+1=9, ...
- rpm、yum命令
一.rpm命令 挂载光盘文件到/media目录: 进去/media目录下的Packages目录: 查看系统已安装的所有rpm包: 查看系统是否安装dhcp软件包: 安装dhcp软件包: 查看dhcp软 ...
- 质数,$\varphi$和$\mu$线性筛
typedef long long ll; bool check[N]; int mu[N],pri[N],tot; ll phi[N]; void init(int lim){ check[]=,p ...
- Eclipse 窗口说明---Eclipse教程第03课
Eclipse 工作台(Workbench) 首先,让我们来看一下Eclipse 作台用户界面,和它里面的各种组件. 工作台是多个窗口的集合.每个窗口包含菜单栏,工具栏,快捷方式栏,以及一个或者多个透 ...
- FileStream流媒体
class Program { static void Main(string[] args) { string source = @"mana.mp4"; string targ ...