[SDOI2017]树点涂色
XVII.[SDOI2017]树点涂色
树剖和LCT学到最后实际上是殊途同归的……就比如说这题,可以用树剖,但是在操作中借鉴了LCT的跳链思想;LCT则因为不能子树修改,按照dfs序需要建出线段树出来,实际上也就是树剖的思想了。
首先讲一下LCT写法。观察到任意时刻,任意一种颜色一定是一条深度递增的链。那么刚好可以被存入LCT的一颗splay中。
因此操作就是直接access(x)
即可。
关于操作,我们可以采用树上差分的思想。观察到答案具有可减性。我们设表示节点到根的路径上颜色段数,则路径的答案即为,其中是因为所在的那段颜色被减了两遍。
操作维护dfs序线段树直接求子树最值即可(类似于树剖操作)。
至于如何维护的值呢?我们思考一下,发现它就是你把它access
时,所经过的splay的数量。老办法,考虑虚子树的信息。我们只有在access
时才会更改虚子树的关系。
我们回忆一下access
时我们干了点什么:
inline void access(int x){
for(register int y=0;x;x=t[y=x].fa)splay(x),rson=y,pushup(x);
}
我们断掉rson
的实边,并增加了y
的实边。
对于y
,少了一个splay,应该整棵子树;对于rson
,多了一个splay,应该整棵子树。
但是!!!别忘了,splay中一切父子关系都是不可靠的。我们必须找到真正的rson
和真正的y
。而真正的rson
和真正的y
,就是以它们为根的splay中,深度最浅的点。
然后我们写出了这样的找根函数:
inline int find(int x){
pushdown(x);
while(lson)pushdown(x),x=lson;
return x;
}
使用这个就能找到正确的子节点。
正确的access
:
inline void access(int x){
for(register int y=0,z;x;x=t[y=x].fa){
splay(x);
if(rson)z=find(rson),modify(1,1,n,dfn[z],dfn[z]+sz[z]-1,1);
rson=y;
if(y)z=find(y),modify(1,1,n,dfn[z],dfn[z]+sz[z]-1,-1);
pushup(x);
}
}
总代码:
#include<bits/stdc++.h>
using namespace std;
int n,m;
//---------------------------------------------------------
struct Edge{
int to,next;
}edge[200100];
struct SegTree{
int tag,mx;
}seg[400100];
struct LCT{
int fa,ch[2];
bool rev;
}t[100100];
//---------------------------------------------------------
int head[100100],cnt,sz[100100];
void ae(int u,int v){
edge[cnt].next=head[u],edge[cnt].to=v,head[u]=cnt++;
edge[cnt].next=head[v],edge[cnt].to=u,head[v]=cnt++;
}
//---------------------------------------------------------
int dfn[100100],dep[100100],tot,rev[100100];
int anc[100100][18];
void dfs(int x,int fa){
dep[x]=dep[fa]+1,dfn[x]=++tot,t[x].fa=fa,sz[x]=1,rev[tot]=x,anc[x][0]=fa;
for(int i=1;(1<<i)<dep[x];i++)anc[x][i]=anc[anc[x][i-1]][i-1];
for(int i=head[x];i!=-1;i=edge[i].next)if(edge[i].to!=fa)dfs(edge[i].to,x),sz[x]+=sz[edge[i].to];
}
int LCA(int u,int v){
if(dep[u]>dep[v])swap(u,v);
for(int i=17;i>=0;i--)if(dep[u]<=dep[v]-(1<<i))v=anc[v][i];
if(u==v)return u;
for(int i=17;i>=0;i--)if(anc[u][i]!=anc[v][i])u=anc[u][i],v=anc[v][i];
return anc[u][0];
}
//---------------------------------------------------------
#define lson x<<1
#define rson x<<1|1
#define mid ((l+r)>>1)
void update(int x){seg[x].mx=max(seg[lson].mx,seg[rson].mx);}
void ADD(int x,int y){seg[x].tag+=y,seg[x].mx+=y;}
void downdate(int x){ADD(lson,seg[x].tag),ADD(rson,seg[x].tag),seg[x].tag=0;}
void build(int x,int l,int r){
if(l==r){seg[x].mx=dep[rev[l]];return;}
build(lson,l,mid),build(rson,mid+1,r),update(x);
}
void modify(int x,int l,int r,int L,int R,int val){
if(l>R||r<L)return;
if(L<=l&&r<=R){ADD(x,val);return;}
downdate(x),modify(lson,l,mid,L,R,val),modify(rson,mid+1,r,L,R,val),update(x);
}
int query(int x,int l,int r,int L,int R){
if(l>R||r<L)return 0;
if(L<=l&&r<=R)return seg[x].mx;
downdate(x);
return max(query(lson,l,mid,L,R),query(rson,mid+1,r,L,R));
}
#undef lson
#undef rson
//---------------------------------------------------------
#define lson t[x].ch[0]
#define rson t[x].ch[1]
inline int identify(int x){
if(x==t[t[x].fa].ch[0])return 0;
if(x==t[t[x].fa].ch[1])return 1;
return -1;
}
inline void pushup(int x){}
inline void REV(int x){t[x].rev^=1,swap(lson,rson);}
inline void pushdown(int x){
if(!t[x].rev)return;
if(lson)REV(lson);
if(rson)REV(rson);
t[x].rev=0;
}
inline void rotate(int x){
register int y=t[x].fa;
register int z=t[y].fa;
register int dirx=identify(x);
register int diry=identify(y);
register int b=t[x].ch[!dirx];
if(diry!=-1)t[z].ch[diry]=x;t[x].fa=z;
if(b)t[b].fa=y;t[y].ch[dirx]=b;
t[y].fa=x,t[x].ch[!dirx]=y;
pushup(y),pushup(x);
}
inline void pushall(int x){if(identify(x)!=-1)pushall(t[x].fa);pushdown(x);}
inline void splay(int x){
pushall(x);
while(identify(x)!=-1){
register int fa=t[x].fa;
if(identify(fa)==-1)rotate(x);
else if(identify(x)==identify(fa))rotate(fa),rotate(x);
else rotate(x),rotate(x);
}
}
inline int find(int x){
pushdown(x);
while(lson)pushdown(x),x=lson;
return x;
}
inline void access(int x){
for(register int y=0,z;x;x=t[y=x].fa){
splay(x);
if(rson)z=find(rson),modify(1,1,n,dfn[z],dfn[z]+sz[z]-1,1);
rson=y;
if(y)z=find(y),modify(1,1,n,dfn[z],dfn[z]+sz[z]-1,-1);
pushup(x);
}
}
#undef lson
#undef rson
//---------------------------------------------------------
int main(){
scanf("%d%d",&n,&m),memset(head,-1,sizeof(head));
for(int i=1,x,y;i<n;i++)scanf("%d%d",&x,&y),ae(x,y);
dfs(1,0),build(1,1,n);
for(int i=1,t1,t2,t3;i<=m;i++){
scanf("%d%d",&t1,&t2);
if(t1==1)access(t2);
if(t1==2){
scanf("%d",&t3);
int lca=LCA(t2,t3);
lca=query(1,1,n,dfn[lca],dfn[lca]);
t2=query(1,1,n,dfn[t2],dfn[t2]);
t3=query(1,1,n,dfn[t3],dfn[t3]);
printf("%d\n",t2+t3-2*lca+1);
}
if(t1==3)printf("%d\n",query(1,1,n,dfn[t2],dfn[t2]+sz[t2]-1));
}
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 【杂谈】分布式事务——高大上的无用知识?