P3703-[SDOI2017]树点涂色【LCT,线段树】
正题
题目链接:https://www.luogu.com.cn/problem/P3703
题目大意
\(n\)个点的一棵树开始所有点有不同的颜色,\(m\)次操作
- 将根节点到\(x\)节点的路径上染上一种新的颜色
- 询问一条路径的不同颜色个数
- 询问一个节点的子树中的一个\(x\)使得\(x\)到根节点的颜色最多。
解题思路
操作\(1\)和\(LCT\)的\(access\)操作很相似。相同颜色之间就是实边,不同颜色之间就是虚边。
操作\(2\)就是之间\(p_x+p_y-2p_{LCA}+1\)就好了,但是考虑到操作\(3\),所以维护一个\(dfn\)序和线段树就可以查询子树最大值了。
之后维护一个\(LCT\),在\(access\)操作切换虚实边的时候修改一下线段树就好了,并且需要注意我们不能直接拿\(Splay\)的根的子树,要找到实际的树中的根,所以\(Splay\)一直往左就好了。
好像还有树链剖分的做法,线段树查询的时候维护一下末尾颜色好像就可以了,这里不多讲(我也不会)
时间复杂度\(O(n\log^2 n)\)
code
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=1e5+10;
struct node{
int to,next;
}a[N<<1];
int n,m,cnt,tot,ls[N],rfn[N],ed[N],fa[N];
int dep[N],son[N],siz[N],top[N];
struct SegTree{
int w[N<<2],lazy[N<<2];
void Downdata(int x){
if(!lazy[x])return;
w[x*2]+=lazy[x];lazy[x*2]+=lazy[x];
w[x*2+1]+=lazy[x];lazy[x*2+1]+=lazy[x];
lazy[x]=0;return;
}
void Change(int x,int L,int R,int l,int r,int val){
if(L==l&&R==r){w[x]+=val;lazy[x]+=val;return;}
int mid=(L+R)>>1;Downdata(x);
if(r<=mid)Change(x*2,L,mid,l,r,val);
else if(l>mid)Change(x*2+1,mid+1,R,l,r,val);
else Change(x*2,L,mid,l,mid,val),Change(x*2+1,mid+1,R,mid+1,r,val);
w[x]=max(w[x*2],w[x*2+1]);return;
}
int Ask(int x,int L,int R,int l,int r){
if(L==l&&R==r)return w[x];
int mid=(L+R)>>1;Downdata(x);
if(r<=mid)return Ask(x*2,L,mid,l,r);
if(l>mid)return Ask(x*2+1,mid+1,R,l,r);
return max(Ask(x*2,L,mid,l,mid),Ask(x*2+1,mid+1,R,mid+1,r));
}
}Tr;
struct LinkCutTree{
int t[N][2],fa[N];
bool Nroot(int x)
{return fa[x]&&(t[fa[x]][0]==x||t[fa[x]][1]==x);}
bool Direct(int x)
{return t[fa[x]][1]==x;}
void Rotate(int x){
int y=fa[x],z=fa[y];
int xs=Direct(x),ys=Direct(y);
int w=t[x][xs^1];
if(Nroot(y))t[z][ys]=x;
t[x][xs^1]=y;t[y][xs]=w;
if(w)fa[w]=y;fa[y]=x;fa[x]=z;
return;
}
void Splay(int x){
while(Nroot(x)){
int y=fa[x];
if(!Nroot(y))Rotate(x);
else if(Direct(x)==Direct(y))
Rotate(y),Rotate(x);
else Rotate(x),Rotate(x);
}
return;
}
int FindRoot(int x){
while(t[x][0])x=t[x][0];
return x;
}
void Access(int x){
for(int y=0;x;y=x,x=fa[x]){
Splay(x);int z=t[x][1];
if(z)z=FindRoot(z),Tr.Change(1,1,n,rfn[z],ed[z],1);
if(y)z=FindRoot(y),Tr.Change(1,1,n,rfn[z],ed[z],-1);
t[x][1]=y;
}
return;
}
}T;
void addl(int x,int y){
a[++tot].to=y;
a[tot].next=ls[x];
ls[x]=tot;return;
}
void dfs1(int x){
siz[x]=1;rfn[x]=++cnt;
dep[x]=dep[fa[x]]+1;T.fa[x]=fa[x];
Tr.Change(1,1,n,cnt,cnt,dep[x]);
for(int i=ls[x];i;i=a[i].next){
int y=a[i].to;
if(y==fa[x])continue;
fa[y]=x;dfs1(y);siz[x]+=siz[y];
if(siz[y]>siz[son[x]])son[x]=y;
}
ed[x]=cnt;
}
void dfs2(int x){
if(son[x]){
top[son[x]]=top[x];
dfs2(son[x]);
}
for(int i=ls[x];i;i=a[i].next){
int y=a[i].to;
if(y==fa[x]||y==son[x])continue;
top[y]=y;dfs2(y);
}
return;
}
int LCA(int x,int y){
while(top[x]!=top[y]){
if(dep[top[x]]<dep[top[y]])
swap(x,y);
x=fa[top[x]];
}
return dep[x]<dep[y]?x:y;
}
int main()
{
// freopen("paint1.in","r",stdin);
scanf("%d%d",&n,&m);
for(int i=1;i<n;i++){
int x,y;
scanf("%d%d",&x,&y);
addl(x,y);addl(y,x);
}
dfs1(1);dfs2(1);
while(m--){
int op,x,y;
scanf("%d%d",&op,&x);
if(op==1)T.Access(x);
else if(op==2){
scanf("%d",&y);
int lca=LCA(x,y);
int p1=Tr.Ask(1,1,n,rfn[x],rfn[x]);
int p2=Tr.Ask(1,1,n,rfn[y],rfn[y]);
int p3=Tr.Ask(1,1,n,rfn[lca],rfn[lca]);
printf("%d\n",p1+p2-p3*2+1);
}
else printf("%d\n",Tr.Ask(1,1,n,rfn[x],ed[x]));
}
return 0;
}
P3703-[SDOI2017]树点涂色【LCT,线段树】的更多相关文章
- [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 ...
- [SDOI2017][bzoj4817] 树点涂色 [LCT+线段树]
题面 传送门 思路 $LCT$ 我们发现,这个1操作,好像非常像$LCT$里面的$Access$啊~ 那么我们尝试把$Access$操作魔改成本题中的涂色 我们令$LCT$中的每一个$splay$链代 ...
- BZOJ 4817 [SDOI2017]树点涂色 (LCT+线段树维护dfs序)
题目大意:略 涂色方式明显符合$LCT$里$access$操作的性质,相同颜色的节点在一条深度递增的链上 用$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 ...
- 【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\)(画图理解).剩下的操作 ...
随机推荐
- WPF Popup 右下角提示框 定时消失 ,以及任意位置定位
------------恢复内容开始------------ 好久没写WPF的博客了,其实有很多心得要总结下,但是懒..... 今天工作需要,需要实现一个 1 右下角的提示窗口,然后过三五秒自动消失这 ...
- 地球坐标系(WGS-84)转火星坐标系(GCJ)
/** * 单点坐标纠偏 */ var pi = 3.14159265358979324; var a = 6378245.0; var ee = 0.00669342162296594323; va ...
- session.flush与transaction.commit
以session的save方法为例来看一个简单.完整的事务流程,如下是代码片段: ---------------------------- Session session = sessionFacto ...
- Spark消费Kafka如何实现精准一次性消费?
1.定义 精确一次消费(Exactly-once) 是指消息一定会被处理且只会被处理一次.不多不少就一次处理. 如果达不到精确一次消费,可能会达到另外两种情况: 至少一次消费(at least onc ...
- js日期处理多少/月/天以前
var date = new Date(); var year = date.getFullYear(); var month = (date.getMonth() + 1) > 10 ? da ...
- new Vue({ render: h => h(App), }).$mount('#app')
这里创建的vue实例没有el属性,而是在实例后面添加了一个$mount('#app')方法. $mount('#app') :手动挂载到id为app的dom中的意思 当Vue实例没有el属性时,则该实 ...
- html调用swf的语句
<div style="width: 1000px; height: 202px; margin-left: auto; margin-right: auto"> &l ...
- Django分页组件——Paginator
from django.core.paginator import Paginator #导入Paginator objects = ['john','paul','george','ringo',' ...
- ☕【Java技术指南】「并发编程专题」Fork/Join框架基本使用和原理探究(基础篇)
前提概述 Java 7开始引入了一种新的Fork/Join线程池,它可以执行一种特殊的任务:把一个大任务拆成多个小任务并行执行. 我们举个例子:如果要计算一个超大数组的和,最简单的做法是用一个循环在一 ...
- java版gRPC实战之一:用proto生成代码
欢迎访问我的GitHub https://github.com/zq2599/blog_demos 内容:所有原创文章分类汇总及配套源码,涉及Java.Docker.Kubernetes.DevOPS ...