题目链接

操作\(1.2\)裸树剖,但是操作\(3\)每个点的答案\(val\)很不好维护。。

如果我们把同种颜色的点划分到同一连通块中,那么向根染色的过程就是Access()

最初所有点间都是虚边,相同颜色点用实边相连。一条边由实边变为虚边时,深度大的点所在子树所有点\(val+1\)(Access()中原先\(x\)的右儿子答案\(+1\),因为\(x\)颜色变了);

由虚边变为实边时,深度大的点所在子树所有点\(val-1\)(\(fa[x]\)颜色与\(x\)相同导致\(fa[x]\)的贡献没了).(其实是因为 实链数量(贡献)就等于虚边数量\(+1\)?无所谓了)

于是操作\(2\)就是\(val[x]+val[y]-2*val[LCA]+1\);3.是在子树中查询最大值。用线段树+DFS序维护。

有些神奇...如果能用树剖维护每个点到其子节点的边,也是可以的,但是不好维护。而LCT就能做到这点。

ps:

  1. 建树是在DFS序上的,所以深度\(dep\)应以DFS序存储,而不是\(dep[to[i]]=dep[x]+1\)(这个是写法问题)。
  2. Access()中更新右子树代表的子树,是要找到深度次大(相连的)的点,即右子树的最左儿子,而不是直接用右子树!
  3. 对于操作二,输出\(val[x]+val[y]-2*val[LCA(x,y)]+1\)就可以了,不需要考虑\(fa[LCA(x,y)]\)。\(x\)的颜色会被计算两次,但应该一定是统计它一次。

原先的代码:

//29372kb	5412ms
#include <cstdio>
#include <cctype>
#include <algorithm>
#define gc() getchar()
#define B 17
const int N=1e5+5; int n,cnt,dep[N],in[N],out[N],Enum,H[N],to[N<<1],nxt[N<<1];
int pos[N],id[N],log2[N<<1],tot,tm,st[N<<1][18];
struct Seg_Tree
{
#define ls l,m,rt<<1
#define rs m+1,r,rt<<1|1 int val[N<<2],mx[N<<2],tag[N<<2];
inline void Update(int rt){
mx[rt]=std::max(mx[rt<<1],mx[rt<<1|1]);
}
inline void Add(int rt,int v){
val[rt]+=v, tag[rt]+=v, mx[rt]+=v;
}
inline void PushDown(int rt){
Add(rt<<1,tag[rt]), Add(rt<<1|1,tag[rt]), tag[rt]=0;
}
void Build(int l,int r,int rt)
{
if(l==r) val[rt]=mx[rt]=dep[l];
else{
int m=l+r>>1;
Build(ls), Build(rs), Update(rt);
}
}
int Query_P(int l,int r,int rt,int pos)
{
if(l==r) return val[rt];
if(tag[rt]) PushDown(rt);
int m=l+r>>1;
if(pos<=m) return Query_P(ls,pos);
return Query_P(rs,pos);
}
int Query_Max(int l,int r,int rt,int L,int R)
{
if(L<=l && r<=R) return mx[rt];
if(tag[rt]) PushDown(rt);
int m=l+r>>1;
if(L<=m)
if(m<R) return std::max(Query_Max(ls,L,R),Query_Max(rs,L,R));
else return Query_Max(ls,L,R);
return Query_Max(rs,L,R);
}
void Modify(int l,int r,int rt,int L,int R,int v)
{
if(L<=l && r<=R) Add(rt,v);
else
{
if(tag[rt]) PushDown(rt);
int m=l+r>>1;
if(L<=m) Modify(ls,L,R,v);
if(m<R) Modify(rs,L,R,v);
Update(rt);
}
}
}t;
namespace LCT
{
#define lson son[x][0]
#define rson son[x][1] int fa[N],son[N][2],sk[N],L[N]/*Attention!*/;
bool rev[N];
inline void Update(int x){
L[x]= lson?L[lson]:x;
}
inline void Rev(int x){
std::swap(lson,rson), rev[x]^=1;
}
inline void PushDown(int x){
if(rev[x]) Rev(lson),Rev(rson),rev[x]=0;
}
inline bool n_root(int x){
return son[fa[x]][0]==x||son[fa[x]][1]==x;
}
void Rotate(int x)
{
int a=fa[x],b=fa[a],l=son[a][1]==x,r=l^1;
if(n_root(a)) son[b][son[b][1]==a]=x;
if(son[x][r]) fa[son[x][r]]=a;
fa[a]=x, fa[x]=b, son[a][l]=son[x][r], son[x][r]=a;
Update(a);
}
void Splay(int x)
{
int t=1,a=x; sk[1]=x;
while(n_root(a)) sk[++t]=a=fa[a];
while(t) PushDown(sk[t--]);
while(n_root(x))
{
if(n_root(a=fa[x])) Rotate(son[a][1]==x^son[fa[a]][1]==a?x:a);
Rotate(x);
}
Update(x);
}
// #define D(A,B) printf("Modify:%d->%d %d\n",x,A,B);
void Access(int x)
{
for(int pre=0; x; x=fa[pre=x])
{
Splay(x);
if(rson) t.Modify(1,cnt,1,in[L[rson]],out[L[rson]],1);//L[]!!!
if(pre) t.Modify(1,cnt,1,in[L[pre]],out[L[pre]],-1);
rson=pre;
}
}
}
inline int read()
{
int now=0;register char c=gc();
for(;!isdigit(c);c=gc());
for(;isdigit(c);now=now*10+c-'0',c=gc());
return now;
}
void AddEdge(int u,int v)
{
to[++Enum]=v, nxt[Enum]=H[u], H[u]=Enum;
to[++Enum]=u, nxt[Enum]=H[v], H[v]=Enum;
}
void DFS(int x,int f)
{
int t=++tm; st[pos[x]=++tot][0]=tm, id[tm]=x;
in[x]=++cnt;
LCT::fa[x]=f;
for(int i=H[x]; i; i=nxt[i])
if(to[i]!=f) dep[cnt+1]=dep[in[x]]+1/*Attention!*/, DFS(to[i],x), st[++tot][0]=t;
out[x]=cnt;
}
void Init_RMQ()
{
for(int i=2; i<=tot; ++i) log2[i]=log2[i>>1]+1;
for(int j=1; j<=log2[tot]; ++j)
for(int i=tot-(1<<j-1); i; --i)
st[i][j]=std::min(st[i][j-1],st[i+(1<<j-1)][j-1]);
}
int LCA(int l,int r)
{
l=pos[l], r=pos[r];
if(l>r) std::swap(l,r);
int k=log2[r-l+1];
return id[std::min(st[l][k],st[r-(1<<k)+1][k])];
}
int Query(int x,int y){
int w=LCA(x,y);// printf("LCA:%d,%d:%d\n",x,y,w);
return t.Query_P(1,cnt,1,in[x])+t.Query_P(1,cnt,1,in[y])-2*t.Query_P(1,cnt,1,in[w])+1;
} int main()
{
n=read();int q=read(),opt,x,y;
for(int u,v,i=1; i<n; ++i) u=read(),v=read(),AddEdge(u,v);
dep[1]=1, DFS(1,0), t.Build(1,cnt,1), Init_RMQ();
while(q--)
{
opt=read(),x=read();
if(opt==1) LCT::Access(x);
else if(opt==2) y=read(), printf("%d\n",Query(x,y));
else printf("%d\n",t.Query_Max(1,cnt,1,in[x],out[x]));
}
return 0;
}

新写的代码(19.2.13):

//14980kb	3456ms
#include <cstdio>
#include <cctype>
#include <algorithm>
//#define gc() getchar()
#define MAXIN 300000
#define gc() (SS==TT&&(TT=(SS=IN)+fread(IN,1,MAXIN,stdin),SS==TT)?EOF:*SS++)
typedef long long LL;
const int N=1e5+5; int n,Enum,H[N],nxt[N<<1],to[N<<1],in[N],out[N],dep[N],fa[N],sz[N],son[N],top[N],A[N];
char IN[MAXIN],*SS=IN,*TT=IN; struct Segment_Tree
{
#define ls rt<<1
#define rs rt<<1|1
#define lson l,m,ls
#define rson m+1,r,rs
#define S N<<2
int mx[S],tag[S];
#undef S
#define Upd(rt,v) mx[rt]+=v, tag[rt]+=v
#define Update(rt) mx[rt]=std::max(mx[ls],mx[rs])
inline void PushDown(int rt)
{
Upd(ls,tag[rt]), Upd(rs,tag[rt]), tag[rt]=0;
}
void Build(int l,int r,int rt)
{
if(l==r) {mx[rt]=A[l]; return;}
int m=l+r>>1;
Build(lson), Build(rson), Update(rt);
}
void Modify(int l,int r,int rt,int L,int R,int v)
{
if(L<=l && r<=R) {Upd(rt,v); return;}
if(tag[rt]) PushDown(rt);
int m=l+r>>1;
if(L<=m) Modify(lson,L,R,v);
if(m<R) Modify(rson,L,R,v);
Update(rt);
}
int Query(int l,int r,int rt,int L,int R)
{
if(L<=l && r<=R) return mx[rt];
if(tag[rt]) PushDown(rt);
int m=l+r>>1;
if(L<=m)
if(m<R) return std::max(Query(lson,L,R),Query(rson,L,R));
else return Query(lson,L,R);
return Query(rson,L,R);
}
int QueryP(int l,int r,int rt,int pos)
{
if(l==r) return mx[rt];
if(tag[rt]) PushDown(rt);
int m=l+r>>1;
return pos<=m ? QueryP(lson,pos) : QueryP(rson,pos);
}
#undef ls
#undef rs
#undef lson
#undef rson
#undef Upd
#undef Update
}T; namespace LCT
{
#define lson son[x][0]
#define rson son[x][1]
int son[N][2],fa[N],L[N]; inline void Init(const int n){
for(int i=1; i<=n; ++i) L[i]=i;
}
inline bool n_root(int x){
return son[fa[x]][0]==x||son[fa[x]][1]==x;
}
inline void Update(int x){
L[x]=lson?L[lson]:x;
}
void Rotate(int x)
{
int a=fa[x],b=fa[a],l=son[a][1]==x,r=l^1;
if(n_root(a)) son[b][son[b][1]==a]=x;
if(son[x][r]) fa[son[x][r]]=a;
fa[x]=b, fa[a]=x, son[a][l]=son[x][r], son[x][r]=a;
Update(a);
}
void Splay(int x)
{
int a;
while(n_root(x))
{
if(n_root(a=fa[x])) Rotate(son[a][0]==x^son[fa[a]][0]==a?x:a);
Rotate(x);
}
Update(x);
}
void Access(int x)
{
for(int pre=0; x; x=fa[pre=x])
{
Splay(x);
if(rson) T.Modify(1,n,1,in[L[rson]],out[L[rson]],1);//L!
if(pre) T.Modify(1,n,1,in[L[pre]],out[L[pre]],-1);
rson=pre;
}
}
} inline int read()
{
int now=0;register char c=gc();
for(;!isdigit(c);c=gc());
for(;isdigit(c);now=now*10+c-48,c=gc());
return now;
}
inline void AE(int u,int v)
{
to[++Enum]=v, nxt[Enum]=H[u], H[u]=Enum;
to[++Enum]=u, nxt[Enum]=H[v], H[v]=Enum;
}
inline int LCA(int u,int v)
{
while(top[u]!=top[v]) dep[top[u]]>dep[top[v]]?u=fa[top[u]]:v=fa[top[v]];
return dep[u]>dep[v]?v:u;
}
void DFS1(int x)
{
static int Index=0;
A[in[x]=++Index]=dep[x];
int mx=0; sz[x]=1;
for(int i=H[x],v; i; i=nxt[i])
if((v=to[i])!=fa[x])
dep[v]=dep[x]+1, fa[v]=x, LCT::fa[v]=x, DFS1(v), sz[x]+=sz[v], sz[v]>mx&&(mx=sz[v],son[x]=v);
out[x]=Index;
}
void DFS2(int x,int tp)
{
top[x]=tp;
if(son[x])
{
DFS2(son[x],tp);
for(int i=H[x],v; i; i=nxt[i])
if((v=to[i])!=fa[x] && v!=son[x]) DFS2(v,v);
}
} int main()
{
int n=read(),q=read(); ::n=n;
for(int i=1; i<n; ++i) AE(read(),read());
dep[1]=1, DFS1(1), DFS2(1,1), T.Build(1,n,1), LCT::Init(n);
for(int x,y; q--; )
switch(read())
{
case 1: LCT::Access(read()); break;
case 2: x=read(), y=read(), printf("%d\n",T.QueryP(1,n,1,in[x])+T.QueryP(1,n,1,in[y])-(T.QueryP(1,n,1,in[LCA(x,y)])<<1)+1); break;
case 3: x=read(), printf("%d\n",T.Query(1,n,1,in[x],out[x])); break;
} return 0;
}

BZOJ.4817.[SDOI2017]树点涂色(LCT DFS序 线段树)的更多相关文章

  1. [BZOJ4817][SDOI2017]树点涂色(LCT+DFS序线段树)

    4817: [Sdoi2017]树点涂色 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 692  Solved: 408[Submit][Status ...

  2. bzoj4817/luogu3703 树点涂色 (LCT+dfs序+线段树)

    我们发现,这个染色的操作他就很像LCT中access的操作(为什么??),然后就自然而然地想到,其实一个某条路径上的颜色数量,就是我们做一个只有access操作的LCT,这条路径经过的splay的数量 ...

  3. P3703 [SDOI2017]树点涂色 LCT维护颜色+线段树维护dfs序+倍增LCA

    \(\color{#0066ff}{ 题目描述 }\) Bob有一棵\(n\)个点的有根树,其中1号点是根节点.Bob在每个点上涂了颜色,并且每个点上的颜色不同. 定义一条路径的权值是:这条路径上的点 ...

  4. [BZOJ3779]重组病毒(LCT+DFS序线段树)

    同[BZOJ4817]树点涂色,只是多了换根操作,分类讨论下即可. #include<cstdio> #include<algorithm> #define lc ch[x][ ...

  5. bzoj 1782: [Usaco2010 Feb]slowdown 慢慢游【dfs序+线段树】

    考虑每头牛到达之后的影响,u到达之后,从1到其子树内的点需要放慢的都多了一个,p为u子树内点的牛ans会加1 用线段树维护dfs序,每次修改子树区间,答案直接单点查询p即可 #include<i ...

  6. [BZOJ4817][SDOI2017]树点涂色:Link-Cut Tree+线段树

    分析 与[BZOJ3779]重组病毒唯一的区别是多了一个链上求实链段数的操作. 因为每条实链的颜色必然不相同且一条实链上不会有两个深度相同的点(好像算法的正确性和第二个条件没什么关系,算了算了),画图 ...

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

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

  8. DFS序+线段树(bzoj 4034)

    题目链接 题目就不多说了. 本题目,可以用dfs序+线段树做:题目给定了一棵树,树上节点告诉了权值.我们可以先将这棵树进行dfs将一棵树变成线性结构:如图 变成这样后,然后就可以用线段树. 操作1:也 ...

  9. BZOJ 3252题解(贪心+dfs序+线段树)

    题面 传送门 分析 此题做法很多,树形DP,DFS序+线段树,树链剖分都可以做 这里给出DFS序+线段树的代码 我们用线段树维护到根节点路径上节点权值之和的最大值,以及取到最大值的节点编号x 每次从根 ...

随机推荐

  1. C++学习2--坦克大战编写-前置知识

    基础班学习的这一个多月里的前三周讲解基础的语法,最后一周需要做坦克大战的项目巩固提高自己掌握的语法知识.这个系列博文主要是为了把学习过程中的知识点总结并记录下来: 开发语言与开发工具:C++,VS20 ...

  2. [转]RJ45接口说明

    [转]http://blog.csdn.net/dog0138/article/details/7016351 1.前言 常见的RJ45接口有两类: 用于以太网网卡.路由器以太网接口等的DTE类型,可 ...

  3. SHA1算法原理

    一.SHA1与MD5差异 SHA1对任意长度明文的预处理和MD5的过程是一样的,即预处理完后的明文长度是512位的整数倍,但是有一点不同,那就是SHA1的原始报文长度不能超过2的64次方,然后SHA1 ...

  4. 【转】深入浅出JMS(三)--ActiveMQ简单的HelloWorld实例

    这篇博文,我们使用ActiveMQ为大家实现一种点对点的消息模型.如果你对点对点模型的认识较浅,可以看一下第一篇博文的介绍. JMS其实并没有想象的那么高大上,看完这篇博文之后,你就知道什么叫简单,下 ...

  5. SharePoint 2013 文档库“样式”变了

    有朋友反馈说文档库的样式变了. 经查证,原来有人修改了视图的"样式":库设置—视图—样式,改为默认即可. 另外,如果编辑页面,编辑web部件的属性,在"杂项"勾 ...

  6. 织梦dedeCMS数据库结构字段说明-简略说明

    dede_addonarticle 附加文章表 aid int(11) 文章编号typeid int(11) 分类栏目编号body mediumtext 文章内容dede_addonflash 附加F ...

  7. saltstack自动化运维系列11基于etcd的saltstack的自动化扩容

    saltstack自动化运维系列11基于etcd的saltstack的自动化扩容 自动化运维-基于etcd加saltstack的自动化扩容# tar -xf etcd-v2.2.1-linux-amd ...

  8. 企业内部在centos7.2系统中必杀技NTP时间服务器及内网服务器时间同步(windows和linux客户端同步)

    网络时间协议NTP(Network Time Protocol)是用于互联网中时间同步的标准互联网协议.NTP的用途是把计算机的时间同步到某些时间标准.目前采用的时间标准是世界协调时UTC(Unive ...

  9. S5PV210的根文件系统制作

    一.移植BusyBox1.下载BusyBox的源代码下载地址:http://www.busybox.net/downloads/,此处下载busybox-1.20.2.tar.bz2.2.解压并进入目 ...

  10. 01 响应式页面-@media介绍,

    我们为什么要写自适应的页面(响应式页面) 众所周知,电脑.平板.手机的屏幕是差距很大的,假如在电脑上写好了一个页面,在电脑上看起来不错,但是如果放到手机上的话,那可能就会乱的一塌糊涂,这时候怎么解决呢 ...