spoj Query on a tree VI(链分治)

本文深入探讨了链分治算法在解决树上动态查询问题的应用,通过实例讲解如何利用链分治维护树上的连通块大小,特别是在节点颜色变化时的高效更新策略。文章详细解释了算法的核心思想、数据结构设计及实现细节。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

传送门
题意简述:给你一棵 n n n个黑白点的树,支持改一个点的颜色,询问跟某个点颜色相同的连通块大小。


思路:
还是链分治 233
f i , 0 / 1 f_{i,0/1} fi,0/1表示 i i i的所有颜色为 0 / 1 0/1 0/1的轻儿子在它们子树中颜色相同的连通块大小。
然后这个可以用来询问子树里的答案,但是要询问的东西是针对全局的。
因此我们沿着重链往上跳更新答案,直到某条链中有点跟要问的颜色不一样。
f i f_i fi表示这个东西:
在这里插入图片描述
对于这棵树,我们的 f B 1 , 0 f_{B1,0} fB1,0就等于跟它颜色一样的 B 2 B2 B2的子树中颜色为 B B B且与 B 2 B2 B2连通的连通块大小,即 1 1 1
而对于 f B 1 , 1 f_{B1,1} fB1,1由于他的颜色不是 W W W,因此 f B 1 , 1 = 0 f_{B1,1}=0 fB1,1=0
对于 B 3 B3 B3假设重儿子是 W 3 W3 W3,那么 f B 3 , 0 = 1 + 1 = 2 , f B 3 , 1 = 0 f_{B3,0}=1+1=2,f_{B3,1}=0 fB3,0=1+1=2,fB3,1=0
这样子就可以用链分治维护要有的信息了。
代码:

#include<bits/stdc++.h>
#define ri register int
#define fi first
#define se second
using namespace std;
const int N=1e5+5;
inline int read(){
	int ans=0;
	char ch=getchar();
	while(!isdigit(ch))ch=getchar();
	while(isdigit(ch))ans=(((ans<<2)+ans)<<1)+(ch^48),ch=getchar();
	return ans; 
}
typedef pair<int,int> pii;
int n,m,fa[N],top[N],bot[N],num[N],pred[N],siz[N],hson[N],tot=0,f[N][2];
vector<int>e[N];
bool col[N];
void dfs1(int p){
	siz[p]=1;
	for(ri i=0,v;i<e[p].size();++i){
		if((v=e[p][i])==fa[p])continue;
		fa[v]=p,dfs1(v),siz[p]+=siz[v];
		if(siz[v]>siz[hson[p]])hson[p]=v;
	}
}
void dfs2(int p,int tp){
	top[p]=tp,bot[tp]=p,pred[num[p]=++tot]=p;
	if(!hson[p])return;
	dfs2(hson[p],tp);
	for(ri i=0,v;i<e[p].size();++i){
		if((v=e[p][i])==fa[p]||v==hson[p])continue;
		dfs2(v,v);
	}
}
namespace SGT{
	#define lc (p<<1)
	#define rc (p<<1|1)
	#define mid (T[p].l+T[p].r>>1)
	struct Val{
		int ls,rs;
		bool f;
		inline void init(const int&x){ls=rs=x;f=x==0?0:1;}
	};
	struct Node{int l,r;Val s[2];}T[N<<2];
	inline Val operator+(const Val&a,const Val&b){
		Val ret;
		ret.f=a.f&b.f;
		ret.ls=a.ls+(a.f?b.ls:0);
		ret.rs=b.rs+(b.f?a.rs:0);
		return ret;
	}
	inline void pushup(int p){
		T[p].s[0]=T[lc].s[0]+T[rc].s[0];
		T[p].s[1]=T[lc].s[1]+T[rc].s[1];
	}
	inline void Set(int p){
		int k=pred[T[p].l];
		T[p].s[col[k]].init(f[k][col[k]]+1);
		T[p].s[col[k]^1].init(0);
	}
	inline void build(int p,int l,int r){
		T[p].l=l,T[p].r=r;
		if(l==r)return;
		build(lc,l,mid),build(rc,mid+1,r);
	}
	inline void update(int p,int k){
		if(T[p].l==T[p].r)return Set(p);
		update(k<=mid?lc:rc,k),pushup(p);
	}
	inline Val query(int p,int ql,int qr,bool f){
		if(ql<=T[p].l&&T[p].r<=qr)return T[p].s[f];
		if(qr<=mid)return query(lc,ql,qr,f);
		if(ql>mid)return query(rc,ql,qr,f);
		return query(lc,ql,qr,f)+query(rc,ql,qr,f); 
	}
}
pii dfs3(int tp){
	for(ri p=tp;p;p=hson[p]){
		for(ri i=0,v;i<e[p].size();++i){
			if((v=e[p][i])==fa[p]||v==hson[p])continue;
			pii tmp=dfs3(v);
			f[p][0]+=tmp.fi,f[p][1]+=tmp.se;
		}
		SGT::update(1,num[p]);
	}
	return pii(SGT::query(1,num[tp],num[bot[tp]],0).ls,SGT::query(1,num[tp],num[bot[tp]],1).ls);
}
inline void update(int p){
	int ft,tp,bt;
	while(p){
		ft=fa[top[p]],tp=top[p],bt=bot[tp];
		if(ft){
			pii tmp=pii(SGT::query(1,num[tp],num[bt],0).ls,SGT::query(1,num[tp],num[bt],1).ls);
			f[ft][0]-=tmp.fi,f[ft][1]-=tmp.se;
		}
		SGT::update(1,num[p]);
		if(ft){
			pii tmp=pii(SGT::query(1,num[tp],num[bt],0).ls,SGT::query(1,num[tp],num[bt],1).ls);
			f[ft][0]+=tmp.fi,f[ft][1]+=tmp.se;
		}
		p=ft;
	}
}
inline int query(int p){
	int ft,tp,bt,ret=0,x=p;
	SGT::Val tmp;
	while(p){
		if(col[x]^col[p])break;
		ft=fa[top[p]],tp=top[p],bt=bot[tp];
		ret=SGT::query(1,num[p],num[bt],col[x]).ls;
		if(p^tp){
			tmp=SGT::query(1,num[tp],num[p]-1,col[x]),ret+=tmp.rs;
			if(!tmp.f)return ret;
		}
		p=ft;
	}
	return ret;
}
int main(){
	n=read();
	for(ri i=1,u,v;i<n;++i)u=read(),v=read(),e[u].push_back(v),e[v].push_back(u);
	dfs1(1),dfs2(1,1),SGT::build(1,1,n),dfs3(1);
	for(ri tt=read(),x;tt;--tt){
		if(read())col[x=read()]^=1,update(x);
		else cout<<query(read())<<'\n';
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值