又一道好题啊qwqqqq

一开始看这个题,还以为是一个树剖的什么毒瘤题目

(不过的确貌似可以用树剖啊)

qwq这真是一道\(LCT\)维护颜色的好题

首先,我们来一个一个操作的考虑。

对于操作\(1\)来说,我们是不是就相当于把\(1~x\)的路径,弄成一个独立的联通块?

哎,这个貌似是\(access(x)\)的操作理念啊QWQ

假设我们用\(LCT\)维护这棵树,一开始就全是虚边,然后对于一次1操作,那么就相当于一次\(access\),那么权值的定义,也就相当于到1的路径上要经过多少个不同的\(splay\)(也就是轻边的数目+1)

起初每个点的权值,都是他的\(deep\)(我们假设根的\(deep是1\)),那么我们该如何询问一条路径上的权值和呢?

QWQ

由于我们的\(LCT\),被当做来维护颜色了,所以自然不能通过\(split\)来算,因为会破坏原来的结构。

qwqqq

这时候就需要题解了

通常,我们求树上路径的方法一般都是树上差分。

那么这个题可以不可以用同样的方法来做呢?

我们来考虑证明一下(感性理解)

假设当前的两个点是\(x,y\),他们的\(LCA\)记为\(l\)

先考虑存在同色的情况

$首先,我们可以知道,l只会和x,y其中一个同色。

显然\(val[x]-val[l]\)表示去掉LCA的这个点,\(x`l\)路径上的颜色个数,然后\(val[y]-val[l]\)同理。

而总的路径条数,显然等于两个式子相加,然后再加上\(LCA\)的颜色(因为\(LCA\)的颜色被减去了两次)。

正好就是我们树上差分的式子

\[val[x]+val[y]-2*val[LCA(x,y)] +1
\]

那么我们现在就剩下最后一个问题了。

怎么维护修改和子树\(max\)呢??

QWQ

既然没有修改,而且树的形态还是固定的。

那就可以直接线段树维护\(dfs\)序啊!

那么对于三操作就相当于子树求max

而对于子树的\(val\)的修改,我们可以发现,只有在每个\(access\)的时候,才会产生修改的影响(因为存在轻重边的切换),直接对应的+1,-1就行

但是有一个要注意的问题就是

!!!!我们修改的时候,要找到当前splay里面深度最小的点,把他和他的子树修改,这样才能起到整个子树都修改的效果

直接上代码


// luogu-judger-enable-o2
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#include<map>
#include<set>
#define mk makr_pair
#define ll long long using namespace std; inline int read()
{
int x=0,f=1;char ch=getchar();
while (!isdigit(ch)) {if (ch=='-') f=-1;ch=getchar();}
while (isdigit(ch)) {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
return x*f;
} const int maxn = 2e5+1e2;
const int maxm = 2*maxn; int point[maxn],nxt[maxm],to[maxm];
int cnt,n,m;
int dfn[maxn],size[maxn],deep[maxn];
int add[4*maxn],g[4*maxn];
int f[maxn][21];
int ch[maxn][3];
int rev[maxn],fa[maxn];
int back[maxn];
int tot; void addedge(int x,int y)
{
nxt[++cnt]=point[x];
to[cnt]=y;
point[x]=cnt;
} void up(int root)
{
g[root]=max(g[2*root],g[2*root+1]);
} void build(int root,int l,int r)
{
if (l==r)
{
g[root]=deep[back[l]];
return;
}
int mid = l+r >> 1;
build(2*root,l,mid);
build(2*root+1,mid+1,r);
up(root);
} void pushdown(int root,int l,int r)
{
if (add[root])
{
add[root*2]+=add[root];
add[2*root+1]+=add[root];
g[2*root]+=add[root];
g[2*root+1]+=add[root];
add[root]=0;
}
} void update(int root,int l,int r,int x,int y,int p)
{
if (x>y) return;
if (x<=l && r<=y)
{
g[root]+=p;
add[root]+=p;
return;
}
pushdown(root,l,r);
int mid = l+r >> 1;
if (x<=mid) update(2*root,l,mid,x,y,p);
if (y>mid) update(2*root+1,mid+1,r,x,y,p);
up(root);
} int query(int root,int l,int r,int x,int y)
{
if (x>y) return 0;
if (x<=l && r<=y)
{
return g[root];
}
pushdown(root,l,r);
int ans=0;
int mid = l+r >> 1;
if (x<=mid) ans=max(ans,query(2*root,l,mid,x,y));
if (y>mid) ans=max(ans,query(2*root+1,mid+1,r,x,y));
return ans;
} void dfs(int x,int faa,int dep)
{
deep[x]=dep;
dfn[x]=++tot;
back[tot]=x;
size[x]=1;
for (int i=point[x];i;i=nxt[i])
{
int p = to[i];
if (p==faa) continue;
fa[p]=x;
f[p][0]=x;
dfs(p,x,dep+1);
size[x]+=size[p];
}
} void init()
{
for (int j=1;j<=20;j++)
for (int i=1;i<=n;i++)
f[i][j]=f[f[i][j-1]][j-1];
} int go_up(int x,int d)
{
for(int i=0;i<=20;i++)
{
if ((1<<i) & d) x=f[x][i];
}
return x;
} int lca(int x,int y)
{
if (deep[x]>deep[y]) x=go_up(x,deep[x]-deep[y]);
else y=go_up(y,deep[y]-deep[x]);
if (x==y) return x;
for (int i=20;i>=0;i--)
{
if (f[x][i]!=f[y][i])
{
x=f[x][i];
y=f[y][i];
}
}
return f[x][0];
} int son(int x)
{
if (ch[fa[x]][0]==x) return 0;
else return 1;
} bool notroot(int x)
{
return ch[fa[x]][0]==x || ch[fa[x]][1]==x;
} void rotate(int x)
{
int y=fa[x],z=fa[y];
int b=son(x),c=son(y);
if (notroot(y)) ch[z][c]=x;
fa[x]=z;
ch[y][b]=ch[x][!b];
fa[ch[x][!b]]=y;
ch[x][!b]=y;
fa[y]=x;
} void splay(int x)
{
while (notroot(x))
{
int y = fa[x],z=fa[y];
int b=son(x),c=son(y);
if(notroot(y))
{
if (b==c) rotate(y);
else rotate(x);
}
rotate(x);
}
} int findroot(int x)
{
while (ch[x][0])
x=ch[x][0];
return x;
}
void access(int x)
{
for (int y=0;x;y=x,x=fa[x])
{
splay(x);
int now = findroot(ch[x][1]);
update(1,1,n,dfn[now],dfn[now]+size[now]-1,1);
now=findroot(y);
update(1,1,n,dfn[now],dfn[now]+size[now]-1,-1);
ch[x][1]=y;
}
} int main()
{
n=read(),m=read();
for (int i=1;i<n;i++)
{
int x=read(),y=read();
addedge(x,y);
addedge(y,x);
}
dfs(1,0,1);
init();
build(1,1,n);
for (int i=1;i<=m;i++)
{
int opt=read();
if (opt==1)
{
int x=read();
access(x);
}
if(opt==2)
{
int x=read(),y=read();
int l = lca(x,y);
cout<<query(1,1,n,dfn[x],dfn[x])+query(1,1,n,dfn[y],dfn[y])-2*query(1,1,n,dfn[l],dfn[l])+1<<"\n";
}
if (opt==3)
{
int x=read();
cout<<query(1,1,n,dfn[x],dfn[x]+size[x]-1)<<"\n";
}
}
return 0;
}

洛谷3703 SDOI2017树点涂色(LCT+线段树+dfs序)的更多相关文章

  1. [Sdoi2017]树点涂色 [lct 线段树]

    [Sdoi2017]树点涂色 题意:一棵有根树,支持x到根染成新颜色,求x到y颜色数,求x子树里点到根颜色数最大值 考场发现这个信息是可减的,但是没想到lct 特意设计成lct的形式! 如何求颜色数? ...

  2. 【BZOJ4817】[Sdoi2017]树点涂色 LCT+线段树

    [BZOJ4817][Sdoi2017]树点涂色 Description Bob有一棵n个点的有根树,其中1号点是根节点.Bob在每个点上涂了颜色,并且每个点上的颜色不同.定义一条路径的权值是:这条路 ...

  3. 【BZOJ4817】【SDOI2017】树点涂色 [LCT][线段树]

    树点涂色 Time Limit: 10 Sec  Memory Limit: 128 MB[Submit][Status][Discuss] Description Bob有一棵n个点的有根树,其中1 ...

  4. BZOJ 4817 [SDOI2017]树点涂色 (LCT+线段树维护dfs序)

    题目大意:略 涂色方式明显符合$LCT$里$access$操作的性质,相同颜色的节点在一条深度递增的链上 用$LCT$维护一个树上集合就好 因为它维护了树上集合,所以它别的啥都干不了了 发现树是静态的 ...

  5. [SDOI2017][bzoj4817] 树点涂色 [LCT+线段树]

    题面 传送门 思路 $LCT$ 我们发现,这个1操作,好像非常像$LCT$里面的$Access$啊~ 那么我们尝试把$Access$操作魔改成本题中的涂色 我们令$LCT$中的每一个$splay$链代 ...

  6. BZOJ4817[Sdoi2017]树点涂色——LCT+线段树

    题目描述 Bob有一棵n个点的有根树,其中1号点是根节点.Bob在每个点上涂了颜色,并且每个点上的颜色不同.定义一条路 径的权值是:这条路径上的点(包括起点和终点)共有多少种不同的颜色.Bob可能会进 ...

  7. bzoj4817 & loj2001 [Sdoi2017]树点涂色 LCT + 线段树

    题目传送门 https://lydsy.com/JudgeOnline/problem.php?id=4817 https://loj.ac/problem/2001 题解 可以发现这个题就是 bzo ...

  8. 【bzoj4817】树点涂色 LCT+线段树+dfs序

    Description Bob有一棵n个点的有根树,其中1号点是根节点.Bob在每个点上涂了颜色,并且每个点上的颜色不同.定义一条路 径的权值是:这条路径上的点(包括起点和终点)共有多少种不同的颜色. ...

  9. BZOJ 4817 [Sdoi2017]树点涂色 ——LCT 线段树

    同BZOJ3779. SDOI出原题,还是弱化版的. 吃枣药丸 #include <map> #include <cmath> #include <queue> # ...

  10. BZOJ 4817: [Sdoi2017]树点涂色(lct+线段树)

    传送门 解题思路 跟重组病毒这道题很像.只是有了一个询问\(2\)的操作,然后询问\(2\)的答案其实就是\(val[x]+val[y]-2*val[lca(x,y)]+1\)(画图理解).剩下的操作 ...

随机推荐

  1. Go定时器--Timer

    目录 前言 Timer 定时器 简介 使用场景 1. 设定超时时间 2. 延迟执行某个方法 Timer对外接口 1. 创建定时器 2. 停止定时器 3. 重置定时器 其他接口 1. After() 2 ...

  2. 100个裁判对n个选手做无并列排名问题探析

    原题:n 个选手(n ≥ 3)参加花样自行车比赛,100 个裁判独立对各选手的表现给出无并列排名.已知对任意三个选手 A.B.C 和任意三个裁判 X.Y.Z 均不会出现如下的情形:X 给出 A > ...

  3. 基于Linux系统Samba服务器的部署

    1.基础信息 用 Internet 文件系统 CIFS(Common Internet File System)是适用于MicrosoftWindows 服务器和客户端的标准文件和打印机共享系统信息块 ...

  4. git02

    Git Gui的使用 Ssh key 介绍及使用 Ssh key介绍 我理解的就是每台电脑上会产生出一个ssh key,然后自己有一个远程账户,但是自己有可能有很多台电脑, 包括家里的电脑还有公司的电 ...

  5. Merchant

      \(get\)二分新用法.   每道题都有答案范围提示,以前只是以为是用来提示用什么类型输出的.   从来没想过直接用它来二分.   这道题真的刷新了我的认知啊......   整道题的复杂度是\ ...

  6. 第04课:使用 VS 管理开源项目

    本节课将介绍 Redis 项目在 Linux 系统中使用 gdb 去调试,这里的调试环境是 CentOS 7.0,但是通常情况下对于 C/C++ 项目我一般习惯使用 Visual Studio 去做项 ...

  7. SpringMVC-初见

    目录 什么是SpringMVC? DispatcherServlet 第一个MVC程序 配置版 Maven可能存在资源过滤的问题 注解版 RestFul和控制器 实现Controller接口 使用注解 ...

  8. 源码解读Dubbo分层设计思想

    一.Dubbo分层整体设计概述 我们先从下图开始简单介绍Dubbo分层设计概念: (引用自Duboo开发指南-框架设计文档) 如图描述Dubbo实现的RPC整体分10层:service.config. ...

  9. Windows安装Docker & Docker-Compose & 配置docker私有仓库

    一定要给windows先创建软连接,不然系统盘会爆表的: mklink /j .docker D:\Administrator\.docker Win7安装Docker Dockerfile # FR ...

  10. lombok时运行编译无法找到get/set方法 看这篇就够了

    今天项目突然运行的时候报错,提示找不到get和set方法,这个时候我就检查了项目,在编译器(idea)是没有报错的.说明编译没问题,只是运行过不去. 后面就开始用我的方法解决这个问题,一步一步排查. ...