Description

Bob有一棵n个点的有根树,其中1号点是根节点。Bob在每个点上涂了颜色,并且每个点上的颜色不同。定义一条路

径的权值是:这条路径上的点(包括起点和终点)共有多少种不同的颜色。Bob可能会进行这几种操作:

1 x:

把点x到根节点的路径上所有的点染上一种没有用过的新颜色。

2 x y:

求x到y的路径的权值。

3 x y:

在以x为根的子树中选择一个点,使得这个点到根节点的路径权值最大,求最大权值。

Bob一共会进行m次操作

Input

第一行两个数n,m。

接下来n-1行,每行两个数a,b,表示a与b之间有一条边。

接下来m行,表示操作,格式见题目描述

1<=n,m<=100000

Output

每当出现2,3操作,输出一行。

如果是2操作,输出一个数表示路径的权值

如果是3操作,输出一个数表示权值的最大值

Sample Input

5 6

1 2

2 3

3 4

3 5

2 4 5

3 3

1 4

2 4 5

1 5

2 4 5

Sample Output

3

4

2

2

Solution

这真的是一道数据结构神题

一步一步来,我们先看如何求答案

对于第2个询问,如果我们知道每个节点到根路径上的答案,那么就变成了经典的树上差分,\(ans(u)+ans(v)-2*ans(lca_{u,v})+1\)

这里解释一下+1,这东西很妙啊。看修改操作,每次都是把一个点到根路径上的颜色都改成一种新的颜色,所以不可能存在 \(u\) 到 \(lca\) (\(lca\) 除外)的路径上与 \(v\) 到 \(lca\) (\(lca\) 除外)的路径上有相同的颜色,路径上也不可能出现颜色断层现象。所以只有两种情况:一是 \(lca\) 上的颜色不与任何它到 \(u\),\(v\) 路径上的颜色相同,这样减去后,因为 \(lca\) 上是一种新的颜色,所以要加1;二是 \(lca\) 上的颜色与它到 \(u\),\(v\) 的某一条路径上连着的一段颜色相同,这样减去之后这连着一段颜色相同的贡献多减了一次,所以还是要加1

关键就是这个修改操作,很精髓,所以这样差分是正确的(可以自己手动画画)

然后对于第3个询问,既然我们维护的就是 \(ans(u)\) 和 \(ans(v)\) ,那么就再找到它的 \(lca\) ,直接三次询问就出答案了

再看第一个修改操作,这不很像LCT的access吗,那就用access维护

而对于 \(ans(u)\) 答案的维护,因为又要维护点,又要维护子树,那就树剖后的线段树吧

然后每次access的时候,如果断开了右儿子的链,那么右儿子与当前点的颜色就不一样了,所以要给右儿子所在的子树的答案加1;而新接的右子树因为从颜色不一样变成了颜色一样,所以要给新的右子树的答案减1

数据结构啊,数据结构。。

#include<bits/stdc++.h>
#define ll long long
#define db double
#define ld long double
const int MAXN=100000+10;
int n,m,e,to[MAXN<<1],nex[MAXN<<1],beg[MAXN],srt[MAXN],end[MAXN],hson[MAXN],cnt,size[MAXN],fa[MAXN],dep[MAXN],iit[MAXN],top[MAXN];
template<typename T> inline void chkmin(T &x,T y){x=(y<x?y:x);}
template<typename T> inline void chkmax(T &x,T y){x=(y>x?y:x);}
template<typename T> inline T min(T x,T y){return x<y?x:y;}
template<typename T> inline T max(T x,T y){return x>y?x:y;}
#define Mid ((l+r)>>1)
#define lson rt<<1,l,Mid
#define rson rt<<1|1,Mid+1,r
struct SEG{
int Mx[MAXN<<2],Ad[MAXN<<2];
inline void PushUp(int rt)
{
Mx[rt]=max(Mx[rt<<1],Mx[rt<<1|1]);
}
inline void PushDown(int rt)
{
Mx[rt<<1]+=Ad[rt];Mx[rt<<1|1]+=Ad[rt];
Ad[rt<<1]+=Ad[rt];Ad[rt<<1|1]+=Ad[rt];
Ad[rt]=0;
}
inline void Build(int rt,int l,int r)
{
if(l==r)Mx[rt]=iit[l];
else
{
Build(lson);Build(rson);
PushUp(rt);
}
}
inline void Update(int rt,int l,int r,int L,int R,int k)
{
if(L<=l&&r<=R)Mx[rt]+=k,Ad[rt]+=k;
else
{
PushDown(rt);
if(L<=Mid)Update(lson,L,R,k);
if(R>Mid)Update(rson,L,R,k);
PushUp(rt);
}
}
inline int QuerySm(int rt,int l,int r,int pos)
{
if(l==r&&r==pos)return Mx[rt];
else
{
PushDown(rt);
if(pos<=Mid)return QuerySm(lson,pos);
else return QuerySm(rson,pos);
}
}
inline int QueryMx(int rt,int l,int r,int L,int R)
{
if(L<=l&&r<=R)return Mx[rt];
else
{
PushDown(rt);
int res=0;
if(L<=Mid)chkmax(res,QueryMx(lson,L,R));
if(R>Mid)chkmax(res,QueryMx(rson,L,R));
return res;
}
}
};
SEG T1;
#undef Mid
#undef lson
#undef rson
#define lc(x) ch[(x)][0]
#define rc(x) ch[(x)][1]
struct LCT{
int ch[MAXN][2],fa[MAXN];
inline bool nroot(int x)
{
return lc(fa[x])==x||rc(fa[x])==x;
}
inline void rotate(int x)
{
int f=fa[x],p=fa[f],c=(rc(f)==x);
if(nroot(f))ch[p][rc(p)==f]=x;
fa[ch[f][c]=ch[x][c^1]]=f;
fa[ch[x][c^1]=f]=x;
fa[x]=p;
}
inline void splay(int x)
{
for(register int y=fa[x];nroot(x);rotate(x),y=fa[x])
if(nroot(y))rotate((lc(y)==x)==(lc(fa[y])==y)?y:x);
}
inline int findroot(int x)
{
while(lc(x))x=lc(x);
return x;
}
inline void access(int x)
{
for(register int y=0,rt;x;x=fa[y=x])
{
splay(x);
if(rc(x))rt=findroot(rc(x)),T1.Update(1,1,n,srt[rt],end[rt],1);
rc(x)=y;
if(rc(x))rt=findroot(rc(x)),T1.Update(1,1,n,srt[rt],end[rt],-1);
}
}
};
LCT T2;
#undef lc
#undef rc
template<typename T> inline void read(T &x)
{
T data=0,w=1;
char ch=0;
while(ch!='-'&&(ch<'0'||ch>'9'))ch=getchar();
if(ch=='-')w=-1,ch=getchar();
while(ch>='0'&&ch<='9')data=((T)data<<3)+((T)data<<1)+(ch^'0'),ch=getchar();
x=data*w;
}
template<typename T> inline void write(T x,char c='\0')
{
if(x<0)putchar('-'),x=-x;
if(x>9)write(x/10);
putchar(x%10+'0');
if(c!='\0')putchar(c);
}
inline void insert(int x,int y)
{
to[++e]=y;
nex[e]=beg[x];
beg[x]=e;
}
inline void dfs1(int x,int f,int d)
{
int ch=0;
fa[x]=f;
size[x]=1;dep[x]=d;
for(register int i=beg[x];i;i=nex[i])
if(to[i]==f)continue;
else
{
dfs1(to[i],x,d+1);
size[x]+=size[to[i]];
if(size[to[i]]>ch)hson[x]=to[i],ch=size[to[i]];
}
}
inline void dfs2(int x,int tp)
{
srt[x]=++cnt;
iit[cnt]=dep[x];
top[x]=tp;
if(hson[x])dfs2(hson[x],tp);
for(register int i=beg[x];i;i=nex[i])
if(to[i]==fa[x]||to[i]==hson[x])continue;
else dfs2(to[i],to[i]);
end[x]=cnt;
}
inline int LCA(int u,int v)
{
while(top[u]!=top[v])
{
if(dep[top[u]]<dep[top[v]])std::swap(u,v);
u=fa[top[u]];
}
return dep[u]<dep[v]?u:v;
}
int main()
{
read(n);read(m);
for(register int i=1;i<n;++i)
{
int u,v;
read(u);read(v);
insert(u,v);insert(v,u);
}
dfs1(1,0,1);dfs2(1,1);
T1.Build(1,1,n);
for(register int i=1;i<=n;++i)T2.fa[i]=fa[i];
while(m--)
{
int opt;
read(opt);
if(opt==1)
{
int x;
read(x);
T2.access(x);
}
if(opt==2)
{
int x,y,lca;
read(x);read(y);lca=LCA(x,y);
write(T1.QuerySm(1,1,n,srt[x])+T1.QuerySm(1,1,n,srt[y])-2*T1.QuerySm(1,1,n,srt[lca])+1,'\n');
}
if(opt==3)
{
int x;
read(x);
write(T1.QueryMx(1,1,n,srt[x],end[x]),'\n');
}
}
return 0;
}

【刷题】BZOJ 4817 [Sdoi2017]树点涂色的更多相关文章

  1. BZOJ 4817: [Sdoi2017]树点涂色(LCT+树剖+线段树)

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

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

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

  3. bzoj 4817: [Sdoi2017]树点涂色

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

  4. BZOJ 4817: [Sdoi2017]树点涂色 LCT+Access的性质+DFS序+线段树

    Code: #include<bits/stdc++.h> #define maxn 200003 #define inf -1000000 using namespace std; vo ...

  5. BZOJ.4817.[SDOI2017]树点涂色(LCT DFS序 线段树)

    题目链接 操作\(1.2\)裸树剖,但是操作\(3\)每个点的答案\(val\)很不好维护.. 如果我们把同种颜色的点划分到同一连通块中,那么向根染色的过程就是Access()! 最初所有点间都是虚边 ...

  6. bzoj 4817: [Sdoi2017]树点涂色 LCT+树链剖分+线段树

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

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

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

  8. bzoj 4817: [Sdoi2017]树点涂色【树链剖分+LCT】

    非常妙的一道题. 首先对于操作一"把点x到根节点的路径上所有的点染上一种没有用过的新颜色",长得是不是有点像LCT中的access操作?进而发现,如果把同一颜色的点连起来作为LCT ...

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

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

随机推荐

  1. python解释 yield 和 Generators(生成器)

    yield 和 Generators(生成器) 转自:http://www.oschina.net/translate/improve-your-python-yield-and-generators ...

  2. 初学node.js-npm使用(2)

    1.安装Node封装模块 安装Node封装模块很重要,因为开发项目中会用到各种各样的功能,这时就需要去下载开源的模块 使用npm install <module_name> module_ ...

  3. React Native移动开发实战-4-Android平台的适配原理

    打开Android开发工具Android Studio,选择菜单 Open an existing AndroidStudio project,打开ch04项目的android文件夹,如图5.8所示. ...

  4. pairwork(黄敬博12061156和黄伟龙12061172)

    结对编程: 结对编程的优缺点: 优点: 1.相互督促,共同为了完成目标而努力: 2.节省时间,通过将疑难问题分开解决,共同讨论,实现了更高效的时间利用率: 3.能力互补,提高代码的质量,同时也提高了测 ...

  5. Daily Scrum (2015/10/31)

    这几天我们组的进度有点慢,剩下这一周的我们必须要加油认真对待. 周末这两天我们是这样安排的: 成员 今日任务 时间 明日任务 符美潇 数据库部分代码的编写 1h 每周小组例会 潘礼鹏 团队博客作业   ...

  6. 第一节 Linux系统简介

    一.Linux定义 Linux 是一个操作系统,就像你多少已经了解的 Windows(xp,7,8)和 Max OS. 操作系统在整个计算机系统中的角色: Linux 是系统调用和内核那两层,直观的来 ...

  7. 团队冲刺——Three

    第三天计划: 季方:学习爬虫的操作,以便后续功能实现: 司宇航:对当天实现的功能进行总的测试: 王金萱:数据库内数据的增删改查以及查看团队博客界面的实现: 马佳慧:学习css初步,进行页面绘制: 第二 ...

  8. 冲刺One之站立会议4 /2015-5-17

    今天我们继续了昨天未完成的部分,把服务器端的在线人数显示做了出来,但是在调试的时候还有一些不可预知的自己也不会改的bug,让我们有点不知所措,启动时间的显示相对来说比较容易实现. 燃尽图4

  9. Task 6.2站立会议一

    今天大家把这两天查的资料都拿出来整合到了一起,并仔细分析了其中的联系和区别. 因为大家每个人的思路都不一样,有各种各样的想法和不同的意见,所以最终统一意见是很难的一个过程.开始大家认我们可以做一个单独 ...

  10. 2018软工实践—Beta冲刺(6)

    队名 火箭少男100 组长博客 林燊大哥 作业博客 Beta 冲鸭鸭鸭! 成员冲刺阶段情况 林燊(组长) 过去两天完成了哪些任务 协调组内工作 最终测试文稿编写 展示GitHub当日代码/文档签入记录 ...