[BZOJ4817][SDOI2017]树点涂色
sol
这个题只要你想清楚了就变成码农题了。
你会发现操作1神似\(LCT\)中的\(access\)
考虑在某一次1操作中,会有若干组父子关系,有的是父子从同色变成了异色,有的是从异色变成了同色。如果我们就把同色的点放在一棵splay里面,那操作就直接对应\(access\)操作了。
我们考虑维护这个东西:每个点到根的路径权值\(val_i\)
如果一对父子的颜色从同色变成异色,那么原树中以那个儿子为根的子树的每个点的权值都会加一;反之,若从异色变同色,那么就会全部减一。而对应到\(access\)中,由同变异的刚好是原实儿子,由异变同的刚好是新实儿子。
子树转化成区间,然后就是经典线段树操作。
对于操作2,答案是\(val_x+val_y-2*val_{lca(u,v)}+1\)
所以你需要写LCA+LCT+支持区间加和区间查最大值的线段树
注意:在\(access\)中,打标记的位置是链顶!
所以要在splay中暴跳一下左儿子找到最左边那个点
code
LCA沉迷树链剖分无法自拔
线段树可以标记永久化
LCT只要写\(access\),连\(rev\)都不用打
#include<cstdio>
#include<algorithm>
using namespace std;
const int N = 100005;
int gi()
{
int x=0,w=1;char ch=getchar();
while ((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
if (ch=='-') w=0,ch=getchar();
while (ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
return w?x:-x;
}
struct edge{int to,next;}a[N<<1];
int n,m,head[N],cnt,pa[N],dep[N],sz[N],son[N],top[N],dfn[N],low[N];
void dfs1(int u,int f)
{
pa[u]=f;dep[u]=dep[f]+1;sz[u]=1;
for (int e=head[u];e;e=a[e].next)
{
int v=a[e].to;if (v==f) continue;
dfs1(v,u);
sz[u]+=sz[v];if (sz[v]>sz[son[u]]) son[u]=v;
}
}
void dfs2(int u,int up)
{
top[u]=up;dfn[u]=++cnt;
if (son[u]) dfs2(son[u],up);
for (int e=head[u];e;e=a[e].next)
if (a[e].to!=pa[u]&&a[e].to!=son[u])
dfs2(a[e].to,a[e].to);
low[u]=cnt;
}
int getlca(int u,int v)
{
while (top[u]^top[v])
{
if (dep[top[u]]<dep[top[v]]) swap(u,v);
u=pa[top[u]];
}
return dep[u]<dep[v]?u:v;
}
int tag[N<<2],mx[N<<2];
void Modify(int x,int l,int r,int ql,int qr,int v)
{
if (l>=ql&&r<=qr)
{
tag[x]+=v;mx[x]+=v;
return;
}
int mid=l+r>>1;
if (ql<=mid) Modify(x<<1,l,mid,ql,qr,v);
if (qr>mid) Modify(x<<1|1,mid+1,r,ql,qr,v);
mx[x]=max(mx[x<<1],mx[x<<1|1])+tag[x];
}
int Query(int x,int l,int r,int ql,int qr)
{
if (l>=ql&&r<=qr) return mx[x];
int mid=l+r>>1,s=-1e9;
if (ql<=mid) s=max(s,Query(x<<1,l,mid,ql,qr));
if (qr>mid) s=max(s,Query(x<<1|1,mid+1,r,ql,qr));
return s+tag[x];
}
int fa[N],ls[N],rs[N];
bool isroot(int x){return ls[fa[x]]!=x&&rs[fa[x]]!=x;}
void R_rotate(int x)
{
int y=fa[x],z=fa[y];
ls[y]=rs[x];
if (rs[x]) fa[rs[x]]=y;
fa[x]=z;
if (y==ls[z]) ls[z]=x;if (y==rs[z]) rs[z]=x;
rs[x]=y;fa[y]=x;
}
void L_rotate(int x)
{
int y=fa[x],z=fa[y];
rs[y]=ls[x];
if (ls[x]) fa[ls[x]]=y;
fa[x]=z;
if (y==ls[z]) ls[z]=x;if (y==rs[z]) rs[z]=x;
ls[x]=y;fa[y]=x;
}
void splay(int x)
{
while (!isroot(x))
{
int y=fa[x],z=fa[y];
if (isroot(y))
if (x==ls[y]) R_rotate(x);
else L_rotate(x);
else
if (y==ls[z])
if (x==ls[y]) R_rotate(y),R_rotate(x);
else L_rotate(x),R_rotate(x);
else
if (x==ls[y]) R_rotate(x),L_rotate(x);
else L_rotate(y),L_rotate(x);
}
}
int getf(int x){while(ls[x])x=ls[x];return x;}
void access(int x)
{
int gg;
for (int y=0;x;y=x,x=fa[x])
{
splay(x);
if (rs[x]) gg=getf(rs[x]),Modify(1,1,n,dfn[gg],low[gg],1);
if (y) gg=getf(y),Modify(1,1,n,dfn[gg],low[gg],-1);
rs[x]=y;
}
}
int main()
{
n=gi();m=gi();
for (int i=1,u,v;i<n;i++)
{
u=gi();v=gi();
a[++cnt]=(edge){v,head[u]};head[u]=cnt;
a[++cnt]=(edge){u,head[v]};head[v]=cnt;
}
dfs1(1,0);cnt=0;dfs2(1,1);
for (int i=1;i<=n;i++) fa[i]=pa[i],Modify(1,1,n,dfn[i],low[i],1);
while (m--)
{
int opt=gi(),x=gi(),y,gg;
if (opt==1) access(x);
if (opt==2)
{
y=gi();
gg=getlca(x,y);
printf("%d\n",Query(1,1,n,dfn[x],dfn[x])+Query(1,1,n,dfn[y],dfn[y])-2*Query(1,1,n,dfn[gg],dfn[gg])+1);
}
if (opt==3) printf("%d\n",Query(1,1,n,dfn[x],low[x]));
}
return 0;
}