bzoj4530: [Bjoi2014]大融合(线段树合并)

传送门
线段树合并菜题。
题意简述: n n n个点,支持连边以及查询一个点所在连通块中经过这个点的路径条数,保证这张图时刻为森林。


思路:
先建出所有操作完之后的树统计出 d f s dfs dfs注意有可能是森林而不是树
然后用线段树合并维护每棵子树的 s i z e size size即可。
为了方便,我们将每个节点的 i n in in作为下标插入线段树中,这样可以方便的统计出一棵子树在 [ i n v , o u t v ] , v ∈ s u b t r e e [in_v,out_v],v\in subtree [inv,outv],vsubtree中的 s i z e size size,然后对于查询的边 ( u , v ) (u,v) (u,v)(假设 u u u是父亲, r t rt rt是这棵子树根节点),那么 a n s = ( s i z e r t − s i z e v ) ∗ s i z e v ans=(size_{rt}-size_v)*size_v ans=(sizertsizev)sizev,然后就可以转化为区间查询了。
代码:

#include
#define ri register int
using namespace std;
inline int read(){
	int ans=0;
	char ch=getchar();
	while(!isdigit(ch))ch=getchar();
	while(isdigit(ch))ans=(ans<<3)+(ans<<1)+(ch^48),ch=getchar();
	return ans;
}
const int N=1e5+5;
int n,q,in[N],out[N],rt[N],tot=0,cnt=0,son[N*20][2],siz[N*20],dep[N],anc[N];
vector<int>e[N];
struct Qur{int x,y,k;}qry[N];
inline void build(int&p,int l,int r,int k){
	siz[p=++cnt]=1,son[p][0]=son[p][1]=0;
	if(l==r)return;
	int mid=l+r>>1;
	k<=mid?build(son[p][0],l,mid,k):build(son[p][1],mid+1,r,k);
}
inline int merge(int x,int y,int l,int r){
	if(!x||!y)return x+y;
	siz[x]+=siz[y];
	if(l==r)return x;
	int mid=l+r>>1;
	son[x][0]=merge(son[x][0],son[y][0],l,mid);
	son[x][1]=merge(son[x][1],son[y][1],mid+1,r);
	return x;
}
inline int query(int p,int l,int r,int ql,int qr){
	if(!p||ql>r||qr<l)return 0;
	if(ql<=l&&r<=qr)return siz[p];
	int mid=l+r>>1;
	if(qr<=mid)return query(son[p][0],l,mid,ql,qr);
	if(ql>mid)return query(son[p][1],mid+1,r,ql,qr);
	return query(son[p][0],l,mid,ql,mid)+query(son[p][1],mid+1,r,mid+1,qr);
}
void dfs(int p,int fa){
	in[p]=++tot;
	for(ri i=0,v;i<e[p].size();++i)if((v=e[p][i])^fa)dep[v]=dep[p]+1,dfs(v,p);
	out[p]=tot;
}
inline int find(int x){return x==anc[x]?x:anc[x]=find(anc[x]);}
int main(){
	n=read(),q=read();
	for(ri i=1;i<=q;++i){
		char op[2];
		scanf("%s",op),qry[i].x=read(),qry[i].y=read(),qry[i].k=op[0]!='A';
		if(!qry[i].k)e[qry[i].x].push_back(qry[i].y),e[qry[i].y].push_back(qry[i].x);
	}
	for(ri i=1;i<=n;++i){
		if(!in[i])dfs(i,0);
		build(rt[i],1,n,in[i]),anc[i]=i;
	}
	for(ri i=1,u,v;i<=q;++i){
		u=qry[i].x,v=qry[i].y;
		if(dep[u]>dep[v])swap(u,v);
		if(!qry[i].k)rt[find(u)]=merge(rt[find(u)],rt[find(v)],1,n),anc[find(v)]=anc[(u)];
		else{
			int x=find(v),y=query(rt[x],1,n,in[v],out[v]);
			cout<<(long long)y*(siz[rt[x]]-y)<<'\n';
		}
	}
	return 0;
}

你可能感兴趣的:(#,线段树合并)