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;
}

最新文章

  1. 机器学习实战笔记(Python实现)-06-AdaBoost
  2. sublime text nodejs set
  3. js 获取元素宽高
  4. android aidl 进程间通信需要注意的地方(android.os.TransactionTooLargeException)
  5. windows注册表学习笔记
  6. HDU 5067 (状态压缩DP+TSP)
  7. XAMPP搭建的几个注意事项
  8. Ajax调用asp.net后台代码
  9. Oracle的TPCC测试,原来也是个作弊的东西...
  10. jdk源码剖析:Synchronized
  11. docker3
  12. 解决git中upstream丢失问题Your branch is based on &#39;origin/xxxx&#39;, but the upstream is gone.
  13. vscode 编辑markdown文件
  14. Java编码常见的Log日志打印问题
  15. linux command 3
  16. 【Selenium】selenium中隐藏元素如何定位?
  17. [ZZ]知名互联网公司Python的16道经典面试题及答案
  18. UrlUtils工具类,Java URL工具类,Java URL链接工具类
  19. 第八章&#160;高级搜索树 (xa3)红黑树:插入
  20. Apache Thrift使用简介

热门文章

  1. 路飞项目 day03 前端配置、后台主页、项目依赖问题
  2. vue相关组件用法
  3. 剑指 Offer II 动态规划
  4. while循环内使用for循环
  5. 通过adb查看手机app的包名
  6. nignx 代理前端服务
  7. The first python article
  8. 《Vue.js 3.x高效前端开发(视频教学版)》源码课件同步教学视频免费下载
  9. vi 快捷键/ctags
  10. GuzzleHttp示例