LCT好题

首先我们考虑实际询问的是什么:

从LCT的角度考虑,如果我们认为一开始树上每一条边都是虚边,把一次涂色看作一次access操作,那么询问的实际就是两个节点间的虚边数量+1和子树中的最大虚边数量!

这种问题显然树上容斥,如果设$dis_{i}$表示$i$到根节点需要经过多少虚边,那么答案显然是$dis_{x}+dis_{y}-2dis_{LCA(x,y)}+1$

那么我们实际只是在维护这个$dis_{i}$

怎么维护?

我们注意到,虚实边的转化对应着access操作,因此我们肯定考虑access中进行维护

注意到在每次access的时候,实际对应着三步操作:

第一步:将节点旋转到根

第二步:将节点与右子树之间的边由实边断为虚边

第三步:将节点与原先节点之间的边由虚边变为实边

那么实边变为虚边对应着虚边数量增加,虚边变为实边对应着虚边数量减少

由于这棵树的形态不变,我们考虑直接用线段树维护dfs序,这样就支持了单点查询$dis$和查询子树中最大的$dis$

在修改的时候,考虑到每次access操作的过程,每一次虚实边的变化实际影响的是一棵子树

而这棵子树的根就是在splay上右子树中最靠左的那个点!

因此我们找到这个根,更新即可,区间修改,单点查询+区间查询即可

贴代码:

#include <cstdio>
#include <cmath>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#include <queue>
#include <stack>
#define rt1 rt<<1
#define rt2 (rt<<1)|1
using namespace std;
struct Seg_tree
{
int lazy,maxv;
}tree[400005];
struct Edge
{
int nxt,to;
}edge[200005];
int head[100005];
int ch[100005][2];
int f[100005];
int fa[100005][25];
int dep[100005];
int fl[100005];
int inr[100005],our[100005],onum[100005];
int cnt=1,tot;
int n,m;
void add(int l,int r)
{
edge[cnt].nxt=head[l];
edge[cnt].to=r;
head[l]=cnt++;
}
void dfs(int x,int fx)
{
dep[x]=dep[fx]+1,fa[x][0]=f[x]=fx,inr[x]=++tot,onum[tot]=x;
for(int i=1;i<=20;i++)fa[x][i]=fa[fa[x][i-1]][i-1];
for(int i=head[x];i;i=edge[i].nxt)
{
int to=edge[i].to;
if(to==fx)continue;
dfs(to,x);
}
our[x]=tot;
}
int LCA(int x,int y)
{
if(dep[x]>dep[y])swap(x,y);
for(int i=20;i>=0;i--)if(dep[fa[y][i]]>=dep[x])y=fa[y][i];
if(x==y)return x;
int ret;
for(int i=20;i>=0;i--)
{
if(fa[x][i]!=fa[y][i])x=fa[x][i],y=fa[y][i];
else ret=fa[x][i];
}
return ret;
}
void buildtree(int rt,int l,int r)
{
if(l==r){tree[rt].maxv=dep[onum[l]];return;}
int mid=(l+r)>>1;
buildtree(rt1,l,mid),buildtree(rt2,mid+1,r);
tree[rt].maxv=max(tree[rt1].maxv,tree[rt2].maxv);
}
void down(int rt)
{
tree[rt1].lazy+=tree[rt].lazy,tree[rt1].maxv+=tree[rt].lazy;
tree[rt2].lazy+=tree[rt].lazy,tree[rt2].maxv+=tree[rt].lazy;
tree[rt].lazy=0;
}
void update(int rt,int l,int r,int lq,int rq,int v)
{
if(l>=lq&&r<=rq){tree[rt].lazy+=v;tree[rt].maxv+=v;return;}
int mid=(l+r)>>1;
if(tree[rt].lazy)down(rt);
if(lq<=mid)update(rt1,l,mid,lq,rq,v);
if(rq>mid)update(rt2,mid+1,r,lq,rq,v);
tree[rt].maxv=max(tree[rt1].maxv,tree[rt2].maxv);
}
int query(int rt,int l,int r,int lq,int rq)
{
if(l>=lq&&r<=rq)return tree[rt].maxv;
int mid=(l+r)>>1;
if(tree[rt].lazy)down(rt);
int ret=0;
if(lq<=mid)ret=max(ret,query(rt1,l,mid,lq,rq));
if(rq>mid)ret=max(ret,query(rt2,mid+1,r,lq,rq));
return ret;
}
bool be_root(int x)
{
if(ch[f[x]][0]==x||ch[f[x]][1]==x)return 0;
return 1;
}
void pushdown(int x)
{
if(fl[x])
{
swap(ch[ch[x][0]][0],ch[ch[x][0]][1]);
swap(ch[ch[x][1]][0],ch[ch[x][1]][1]);
fl[ch[x][0]]^=1,fl[ch[x][1]]^=1;
fl[x]=0;
}
}
void repush(int x)
{
if(!be_root(x))repush(f[x]);
pushdown(x);
}
void rotate(int x)
{
int y=f[x],z=f[y],k=(ch[y][1]==x);
if(!be_root(y))ch[z][ch[z][1]==y]=x;
f[x]=z;
ch[y][k]=ch[x][!k],f[ch[x][!k]]=y;
ch[x][!k]=y,f[y]=x;
}
void splay(int x)
{
repush(x);
while(!be_root(x))
{
int y=f[x],z=f[y];
if(!be_root(y))
{
if((ch[y][1]==x)^(ch[z][1]==y))rotate(x);
else rotate(y);
}
rotate(x);
}
}
int get_root(int x)
{
while(ch[x][0])x=ch[x][0];
return x;
}
void access(int x)
{
int y=0;
while(x)
{
splay(x);
if(ch[x][1])
{
int rt=get_root(ch[x][1]);
update(1,1,n,inr[rt],our[rt],1);
}
ch[x][1]=y;
if(y)
{
int rt=get_root(y);
update(1,1,n,inr[rt],our[rt],-1);
}
y=x,x=f[x];
}
}
inline int read()
{
int f=1,x=0;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
int main()
{
n=read(),m=read();
for(int i=1;i<n;i++)
{
int x=read(),y=read();
add(x,y),add(y,x);
}
dfs(1,0),buildtree(1,1,n);
while(m--)
{
int typ=read();
if(typ==1)
{
int x=read();
access(x);
}else if(typ==2)
{
int x=read(),y=read(),fa=LCA(x,y);
int s1=query(1,1,n,inr[x],inr[x]),s2=query(1,1,n,inr[y],inr[y]),s3=query(1,1,n,inr[fa],inr[fa]);
printf("%d\n",s1+s2-2*s3+1);
}else
{
int x=read();
printf("%d\n",query(1,1,n,inr[x],our[x]));
}
}
return 0;
}

bzoj 4817的更多相关文章

  1. AC日记——[SDOI2017]树点涂色 bzoj 4817

    4817 思路: 跪烂大佬 代码: #include <bits/stdc++.h> using namespace std; #define maxn 500005 struct Tre ...

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

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

  3. [BZOJ 4817] [SDOI 2017] 树点涂色

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

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

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

  5. 【刷题】BZOJ 4817 [Sdoi2017]树点涂色

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

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

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

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

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

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

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

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

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

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

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

随机推荐

  1. C#封装FluentValidation

    FluentValidation是一个非常强大的用于构建强类型验证规则的 .NET 框架,帮程序员解决了繁琐的校验问题,用起来非常爽,但我还是遇到了一件非常不爽的事情,如下代码所示: public c ...

  2. unctfWP

    web: 签到:,更改学号,找规律,用笔记本记录出现的数据. 我太喜欢哔哩哔哩大学啦--中北大学:就往下面翻找flag,就会看见一个flag的语句,这个就是答案. ezgame-浙江师范大学:这个就是 ...

  3. 如何用python将txt中的package批量安装

    第一步:cd 到目标路径 第二步:新建一个requirement.txt文档,将所有要下载的包一一罗列出来(需要指定版本的话,可以用==表明) 第三步:输入命令  pip install -r req ...

  4. Pytorch实战学习(二):用Pytorch实现逻辑回归

    <PyTorch深度学习实践>完结合集_哔哩哔哩_bilibili 用Pytorch实现逻辑回归 Logistic Regression 从线性回归 →​ 逻辑回归 1.分类问题 计算属于 ...

  5. hdu 4283You Are the One

    The TV shows such as You Are the One has been very popular. In order to meet the need of boys who ar ...

  6. sleep(0)的意义

    Thread.Sleep(0) 并非是真的要线程挂起0毫秒,意义在于这次调用Thread.Sleep(0)的当前线程确实的被冻结了一下,让其他线程有机会优先执行. Thread.Sleep(0) 是你 ...

  7. 开启Runjar , 使用beeline连接hive

    要先开启hadoop服务 进入root用户hive目录 输入bin/hiveservices.sh stop          停止 输入bin/hiveservices.sh start      ...

  8. 【Java学习Day07】标识符

    标识符 Java使用的组成部分的需要名字.类名.变量名以及方法名都被称为标识符 标识符的注意点 所有的标识符都应该以字母(A-Z或者a-z),美元符($).或者下划线(_)开始 首字符之后可以是字母( ...

  9. 【笔记】archlinux缺少部分常用工具

    安装archlinux之后发现缺少很多常用工具 比如ifconfig ftp等 ifconfig需要安装net-tools nslookup需要dnsutils ftp需要inetutils 另外安装 ...

  10. Scala集合总结

    Scala同时支持可变集合和不可变集合,包含两个包: 可变集合:scala.collection.mutable 不可变集合:scala.collection.immutable Scala默认采用不 ...