[SDOI2017]树点涂色

题面描述

传送门

思路

代码居然出奇的短

想都不想 LCT ⁡ \operatorname{LCT} LCT

首先观察一下 o p t = 1 opt=1 opt=1,这不就是一个奇奇怪怪♂ 的 a c c e s s access access吗?想想 a c c e s s access access的操作,实际上就是实虚边的切换。

由于建树时是全部虚边,恰好就对应每个点的颜色不一样,那么每个点到根节点的权值就恰好是它的深度( d e p [ r t ] = 1 dep[rt]=1 dep[rt]=1)。

a c c e s s ( x ) access(x) access(x),将 x x x r t rt rt的路径成为实边,也就是颜色相同了。但考虑到虚边变成实边了,当前这个 x x x的右儿子需要发生改变,那么,原来的右儿子子树中到根节点的权值都需要 − 1 -1 1,可是 LCT ⁡ \operatorname{LCT} LCT不滋磁修改子树操作鸭。

那就大力 segment tree ⁡ \operatorname{segment ~tree} segment tree线段树吧,但是怎么将其转化为一个区间呢?递归建树时维护一下dfs序即可。

o p t = 2 opt=2 opt=2时,大力求出 l c a ( x , y ) lca(x,y) lca(x,y),答案即为 c o l [ x ] + c o l [ y ] − c o l [ l c a ( x , y ) ] ∗ 2 + 1 col[x]+col[y]-col[lca(x,y)]*2+1 col[x]+col[y]col[lca(x,y)]2+1

o p t = 3 opt=3 opt=3时,线段树乱搞。

AC code

#include<cstdio>
#include<algorithm>
#include<cstdlib>
#include<cstring>
#include<cstdlib>
#define gc getchar()
using namespace std;
const int N=1e5+10;
inline void qr(int &x)
{
	x=0;char c=gc;int f=1;
	while(c<'0'||c>'9'){if(c=='-')f=-1;c=gc;}
	while(c>='0'&&c<='9'){x=x*10+(c^48);c=gc;}
	x*=f;
}
void qw(int x)
{
	if(x<0)x=-x,putchar('-');
	if(x/10)qw(x/10);
	putchar(x%10+48);
}
int st[N],ed[N],dep[N],tmp[N],fa[N][19],tp;
struct SGT{int l,r,mx,ad;}tr[N<<2];
#define pushdown(p) if(tr[p].ad){change(p<<1,tr[p].l,mid,tr[p].ad);change(p<<1|1,mid+1,tr[p].r,tr[p].ad);tr[p].ad=0;}
void update(int p){tr[p].mx=max(tr[p<<1].mx,tr[p<<1|1].mx);}
void build(int p,int l,int r)
{
	if(l==r){tr[p].l=tr[p].r=l;tr[p].mx=tmp[l];return;}
	int mid=(l+r)>>1;tr[p].l=l,tr[p].r=r;
	build(p<<1,l,mid);build(p<<1|1,mid+1,r);
	update(p);
}
void change(int p,int l,int r,int ad)
{
	if(l<=tr[p].l&&r>=tr[p].r){tr[p].mx+=ad;tr[p].ad+=ad;return ;}
	int mid=(tr[p].l+tr[p].r)>>1;
	pushdown(p);
	if(l<=mid)change(p<<1,l,r,ad);
	if(r>mid)change(p<<1|1,l,r,ad);
	update(p);
}
int query(int p,int l,int r)
{
	if(l<=tr[p].l&&r>=tr[p].r)return tr[p].mx;
	int mid=(tr[p].l+tr[p].r)>>1,val=0;pushdown(p);
	if(l<=mid)val=max(val,query(p<<1,l,r));
	if(r>mid)val=max(val,query(p<<1|1,l,r));
	return val;
}
int query(int p,int x)
{
	if(tr[p].l==tr[p].r)return tr[p].mx;
	int mid=(tr[p].l+tr[p].r)>>1;pushdown(p);
	if(x<=mid)return query(p<<1,x);
	else return query(p<<1|1,x);
}
struct LCT{int f,son[2];}t[N];int rt;
inline bool nroot(int p){return t[t[p].f].son[0]==p||t[t[p].f].son[1]==p;}
void rotate(int p,int w)
{
	int f=t[p].f,gf=t[f].f;
	int r=t[p].son[w],R=f;t[R].son[w^1]=r;if(r)t[r].f=R;
	r=p;R=gf;if(nroot(f))t[R].son[t[R].son[1]==f]=r;t[r].f=R;
	r=f;R=p;t[R].son[w]=r;t[r].f=R;
}
void splay(int p)
{
	while(nroot(p))
	{
		int f=t[p].f,gf=t[f].f;
		if(!nroot(f))rotate(p,t[f].son[0]==p);
		else 
		{
			if(t[f].son[0]==p&&t[gf].son[0]==f)rotate(f,1),rotate(p,1);
			else if(t[f].son[0]==p&&t[gf].son[1]==f)rotate(p,1),rotate(p,0);
			else if(t[f].son[1]==p&&t[gf].son[0]==f)rotate(p,0),rotate(p,1);
			else rotate(f,0),rotate(p,0);
		}
	}
}
int find(int x){while(t[x].son[0])x=t[x].son[0];return x;}
void access(int x)
{
	for(int y=0,w;x;x=t[y=x].f)
	{
		splay(x);if(t[x].son[1])w=find(t[x].son[1]),change(1,st[w],ed[w],1);
		if((t[x].son[1]=y))w=find(y),change(1,st[w],ed[w],-1);
	}
}
struct edge{int y,next;}a[N<<1];int len,last[N];
void ins(int x,int y){a[++len]=(edge){y,last[x]};last[x]=len;}
void dfs(int x)
{
	st[x]=++tp;t[x].f=fa[x][0];tmp[tp]=tmp[st[fa[x][0]]]+1;
	for(int i=1;i<=18;i++)fa[x][i]=fa[fa[x][i-1]][i-1];
	for(int k=last[x],y;k;k=a[k].next)
	{
		if(t[x].f==(y=a[k].y))continue;
		dep[y]=dep[x]+1;fa[y][0]=x;dfs(y);
	}
	ed[x]=tp;
}
int lca(int x,int y)
{
	if(dep[x]>dep[y])swap(x,y);
	for(int i=18;i>=0;i--)
		if(dep[fa[y][i]]>=dep[x])y=fa[y][i];
	if(x==y)return x;
	for(int i=18;i>=0;i--)
		if(fa[x][i]!=fa[y][i])x=fa[x][i],y=fa[y][i];
	return fa[x][0];
}
int main()
{
	int n,m;qr(n),qr(m);len=0;memset(last,0,sizeof(last));
	for(int i=1,x,y;i<n;i++)qr(x),qr(y),ins(x,y),ins(y,x);
	tp=0;dfs(1);build(1,1,n);
	for(int i=1,opt,x,y;i<=m;i++)
	{
		qr(opt),qr(x);
		if(opt==1)access(x);
		else if(opt==2)qr(y),qw(query(1,st[x])+query(1,st[y])-2*query(1,st[lca(x,y)])+1),puts("");
		else qw(query(1,st[x],ed[x])),puts("");
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值