Luogu3703 [SDOI2017]树点涂色

Link

Solution

考虑用\(LCT\)维护颜色。每一个\(splay\)维护的是两两不同的颜色。

第一个操作可以实际上就是\(access\)操作。

第二个操作问路径上的颜色种类数。第三个问子树内颜色种类的\(max\)

因为是静态树,不能再把路径\(split\)出来了。考虑差分回答:\(ans=cnt[a]+cnt[b]-2\times cnt[lca(a,b)]+1\)

\(cnt[]\)实际上就是点到根路径上的\(splay\)的数量,也是虚边数量+1。

初始时\(cnt[]\)就是该点深度。因为是静态树,没有\(link\),所以改变虚边情况的只有\(access\)

\(access\)操作中每向上走一个\(splay\),原来的右儿子边变成虚边,\(x-y\)的边变成实边。所以会让\(x\)原来的右儿子所在子树\(cnt\)整体+1,让\(y\)所在的子树\(cnt\)整体-1。

看到子树,应该想到dfs序。子树的\(dfs\)序是连续的一段。整体修改就是区间修改,询问\(max\)就是区间求\(max\),线段树维护就行了。

\(lca\)随便怎么求,树剖就行,还可以顺便求出dfs序。

所以思路就是\(LCT\)维护颜色(一个\(splay\)代表一种颜色),树剖+dfs序+线段树回答询问。

Code

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define REP(i,a,b) for(int i=(a),ed=(b);i<=ed;++i)
inline int read(){
	register int x=0,f=1;register char ch=getchar();
	while(!isdigit(ch)){if(ch=='-')f=0;ch=getchar();}
	while(isdigit(ch)){x=x*10+(ch^'0');ch=getchar();}
	return f?x:-x;
}

const int N=1e5+10;
int n,m,tp[N],siz[N],son[N],dep[N],dfn[N],dfc,idf[N];
vector<int> E[N];

namespace SegmentTree{
#define ls(p) p<<1
#define rs(p) p<<1|1
	int mx[N<<2],tag[N<<2];
	inline void pushup(int p){mx[p]=max(mx[ls(p)],mx[rs(p)]);}
	inline void pushdown(int p){
		if(!tag[p])return;
		tag[ls(p)]+=tag[p];tag[rs(p)]+=tag[p];
		mx[ls(p)]+=tag[p];mx[rs(p)]+=tag[p];
		tag[p]=0;
	}
	inline void build(int p,int l,int r){
		if(l==r){mx[p]=dep[idf[l]];return;}
		int mid=(l+r)>>1;
		build(ls(p),l,mid),build(rs(p),mid+1,r);
		pushup(p);
	}
	inline void modify(int p,int l,int r,int L,int R,int v){
		if(L<=l&&r<=R){mx[p]+=v;tag[p]+=v;return;}
		pushdown(p);int mid=(l+r)>>1;
		if(L<=mid)modify(ls(p),l,mid,L,R,v);
		if(R>mid)modify(rs(p),mid+1,r,L,R,v);
		pushup(p);
	}
	inline int query(int p,int l,int r,int L,int R){
		if(L<=l&&r<=R)return mx[p];
		pushdown(p);int mid=(l+r)>>1,ans=0;
		if(L<=mid)ans=max(ans,query(ls(p),l,mid,L,R));
		if(R>mid)ans=max(ans,query(rs(p),mid+1,r,L,R));
		pushup(p);return ans;
	}
}using namespace SegmentTree;

namespace TCP{
	int fa[N];
	inline void dfs(int nw,int dad){
		fa[nw]=dad;dep[nw]=dep[dad]+1;siz[nw]=1;
		for(int to:E[nw])
			if(to^dad){
				dfs(to,nw);siz[nw]+=siz[to];
				if(siz[to]>siz[son[nw]])son[nw]=to;
			}
	}
	inline void dfs(int nw){
		if(!tp[nw])tp[nw]=nw;
		dfn[nw]=++dfc;idf[dfc]=nw;
		if(son[nw]){tp[son[nw]]=tp[nw];dfs(son[nw]);}
		for(int to:E[nw])
			if(to^fa[nw]&&to^son[nw])dfs(to);
	}
	inline int lca(int x,int y){
		while(tp[x]^tp[y]){
			if(dep[tp[x]]<=dep[tp[y]])y=fa[tp[y]];
			else x=fa[tp[x]];
		}
		return dep[x]>=dep[y]?y:x;
	}
}using TCP::lca;

namespace LCT{
	int fa[N],ch[N][2];
	inline int getdir(int x){return x==ch[fa[x]][1];}
	inline bool noroot(int x){return x==ch[fa[x]][0]||x==ch[fa[x]][1];}
	inline void rotate(int x){
		int f=fa[x],p=fa[f],k=getdir(x),s=ch[x][!k];
		ch[x][!k]=f;ch[f][k]=s;if(noroot(f))ch[p][getdir(f)]=x;
		fa[f]=x;fa[x]=p;if(s)fa[s]=f;
	}
	inline void splay(int x){
		for(int f=fa[x];noroot(x);rotate(x),f=fa[x])
			if(noroot(f))rotate(getdir(f)==getdir(x)?f:x);
	}
	inline int findroot(int x){
		while(ch[x][0])x=ch[x][0];
		return x;
	}
	inline void access(int x){
		for(int y=0;x;y=x,x=fa[x]){
			splay(x);
			int a=findroot(ch[x][1]),b=findroot(y);
			if(ch[x][1])modify(1,1,n,dfn[a],dfn[a]+siz[a]-1,1);
			if(y)modify(1,1,n,dfn[b],dfn[b]+siz[b]-1,-1);
			ch[x][1]=y;
		}
	}
}using LCT::access;

int main(){
//	freopen("in.in","r",stdin);freopen("out.out","w",stdout);
	n=read(),m=read();
	REP(i,1,n-1){
		int x=read(),y=read();
		E[x].push_back(y),E[y].push_back(x);
	}
	dep[1]=1;TCP::dfs(1,0);TCP::dfs(1);build(1,1,n);
	REP(i,1,n)LCT::fa[i]=TCP::fa[i];
	REP(i,1,m){
		int op=read();int x=read();
		if(op==1)access(x);
		else if(op==2){
			int y=read(),p=lca(x,y),ans=0;
			ans+=query(1,1,n,dfn[x],dfn[x]);
			ans+=query(1,1,n,dfn[y],dfn[y]);
			ans-=query(1,1,n,dfn[p],dfn[p])*2;
			printf("%d\n",ans+1);
		}
		else printf("%d\n",query(1,1,n,dfn[x],dfn[x]+siz[x]-1));
	}
	return 0;
}
posted @ 2020-01-03 15:11  Fruitea  阅读(141)  评论(0编辑  收藏  举报