P3703 [SDOI2017] 树点涂色 【LCT+dfs序+线段树】

题面


SOL

维护颜色集合? 树上莫队?好像做不了3操作,只有30分。。。

o p 1 op1 op1

若进行链修改,相当于把 x x x到根路径拼成了一段,一开始点 i i i到根的权值为 d e p [ i ] dep[i] dep[i],相当于每一个点都是独立的一段。那么点到根的权值就是该路径上的 “总段数”。

那我们就用维护每一个点到根的权值。

但是我们还是不能快速处理 o p 1 op1 op1。思考 L C T LCT LCT a c c e s s access access,不是把点到根的路径拼成了一个 s p l a y splay splay吗?
所以我们用 L C T LCT LCT维护 o p 1 op1 op1的修改,每一 a c c e s s access access时,会有重儿子被分开,轻儿子被加入,分开就子树 + 1 +1 +1,加入就子树 − 1 -1 1

注意这里要再写一个 d f s dfs dfs序上的线段树。因为splay只能维护子树信息,不能修改。

注意, s p l a y splay splay维护的是一段点,如果要找子树的根,需要找到该 s p l a y splay splay中深度最浅的点

o p 2 : op2: op2

a n s = v a l [ u ] + v a l [ v ] − 2 ∗ v a l [ f a ] + 1 ans=val[u]+val[v]-2*val[fa]+1 ans=val[u]+val[v]2val[fa]+1

o p 3 : op3: op3

子树求max

复杂度: O ( n l o g 2 n ) O(nlog^2n) O(nlog2n


CODE

//羽毛球打得我手好累,,故代码中变量名从简+各种压行。。

#include<bits/stdc++.h>
using namespace std;
#define sf scanf
#define pf printf
#define ll long long
#define cs const
#define db double
#define ri register int
#define gc getchar()
#define in red()
inline int red(){
	int num=0,f=1;char c=gc;
	for(;!isdigit(c);c=gc)if(c=='-')f=-1;
	for(;isdigit(c);c=gc)num=(num<<1)+(num<<3)+(c^48);
	return num*f;
}
cs int N=1e5+10;
//dfs序 + 欧拉序 
int fa[N],siz[N],dep[N],dfn[N],tot=0,st[20][N<<1],Log[N<<1],cnt=0,pos[N],rev[N],n,m;
vector<int> g[N];
void dfs(int u){
	dep[u]=dep[fa[u]]+1;siz[u]=1;dfn[u]=++tot;rev[tot]=u;st[0][++cnt]=u;pos[u]=cnt;
	for(ri i=g[u].size()-1;i>=0;--i){
		int v=g[u][i];
		if(v==fa[u])continue;
		fa[v]=u;dfs(v);siz[u]+=siz[v];
		st[0][++cnt]=u;
	}
}
inline int _min(int a,int b){return dep[a]<dep[b] ? a:b;}
inline void init(){
	dfs(1);
	Log[1]=0;
	for(ri i=2;i<=cnt;++i)Log[i]=Log[i>>1]+1;
	for(ri i=1;i<=Log[cnt];++i)
		for(ri j=1;j+(1<<i)-1<=cnt;++j)
			st[i][j]=_min(st[i-1][j],st[i-1][j+(1<<i-1)]);
}
inline int lca(int x,int y){
	int fx=min(pos[x],pos[y]),fy=max(pos[x],pos[y]),k=Log[fy-fx+1];
	return _min(st[k][fx],st[k][fy-(1<<k)+1]);
}
//线段树
#define lc (p<<1)
#define rc ((p<<1)|1)
int mx[N<<2],tag[N<<2];
inline void PN(int p,int k){mx[p]+=k;tag[p]+=k;}
inline void PD(int p){if(tag[p])PN(lc,tag[p]),PN(rc,tag[p]);tag[p]=0;}
inline void PU(int p){mx[p]=max(mx[lc],mx[rc]);}
inline void up(int p,int l,int r,int ql,int qr,int k){
	if(ql<=l&&r<=qr)return PN(p,k);
	PD(p);int mid=(l+r)>>1;
	if(ql<=mid)up(lc,l,mid,ql,qr,k);if(qr>mid)up(rc,mid+1,r,ql,qr,k);
	PU(p);
}
inline int qy(int p,int l,int r,int ql,int qr){
	if(ql<=l&&r<=qr)return mx[p];
	PD(p);int mid=(l+r)>>1,res=0;
	if(ql<=mid)res=max(res,qy(lc,l,mid,ql,qr));if(qr>mid)res=max(res,qy(rc,mid+1,r,ql,qr));
	return res;
}
inline void BD(int p,int l,int r){
	if(l==r)return mx[p]=dep[rev[l]],void();
	int mid=(l+r)>>1;BD(lc,l,mid);BD(rc,mid+1,r);
	PU(p);
}
//LCT
int ch[N][2];
inline int CK(int u){return ch[fa[u]][0]^u&&ch[fa[u]][1]^u? -1 : ch[fa[u]][1]==u; }
inline void rotate(int x){
	int y=fa[x],z=fa[y],d=CK(x),d1=CK(y);
	if(~d1)ch[z][d1]=x;
	fa[x]=z;fa[ch[x][d^1]]=y;ch[y][d]=ch[x][d^1];ch[x][d^1]=y;fa[y]=x;
}
inline void splay(int x){
	while(~CK(x)){
		if(~CK(fa[x]))rotate(CK(fa[x])^CK(x) ? x : fa[x]);
		rotate(x);
	}
}
inline int find(int p){return ch[p][0] ? find(ch[p][0]) : p ;}
inline void access(int x){
	for(ri y=0;x;y=x,x=fa[x]){
		splay(x);int tmp;
		if(tmp=find(ch[x][1]))up(1,1,n,dfn[tmp],dfn[tmp]+siz[tmp]-1,1);
		if(tmp=find(ch[x][1]=y))up(1,1,n,dfn[tmp],dfn[tmp]+siz[tmp]-1,-1);
	}
}
//操作
inline void op1(int x){access(x);}
inline int op2(int x,int y){int fa=lca(x,y);return 1+qy(1,1,n,dfn[x],dfn[x])+qy(1,1,n,dfn[y],dfn[y])-(qy(1,1,n,dfn[fa],dfn[fa])<<1);}
inline int op3(int x){return qy(1,1,n,dfn[x],dfn[x]+siz[x]-1);}

signed main(){
//	freopen("data.in","r",stdin);
	n=in;m=in;
	for(ri i=1;i<n;++i){int u=in,v=in;g[u].push_back(v);g[v].push_back(u);}
	init();BD(1,1,n);
	while(m--){
		int op=in;
		if(op==1)op1(in);
		if(op==2)cout<<op2(in,in)<<'\n';
		if(op==3)cout<<op3(in)<<'\n';
	}
	return 0;
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值