[FROM WOJ]#2978 「BJOI2014」大融合

#2978 「BJOI2014」大融合

题面
小强要在 N 个孤立的星球上建立起一套通信系统。这套通信系统就是连接 N 个点的一个树。这个树的边是一条一条添加上去的。在某个时刻,一条边的负载就是它所在的当前能够联通的树上路过它的简单路径的数量。
现在,你的任务就是随着边的添加,动态的回答小强对于某些边的负载的询问。

输入
第一行包含两个整数 N,Q,表示星球的数量和操作的数量。星球从 1 开始编号。
接下来的 q 行,每行是如下两种格式之一:
A x y 表示在 x 和 y 之间连一条边。保证之前 x 和 y 是不联通的。
Q x y 表示询问 (x,y) 这条边上的负载。保证 x 和 y 之间有一条边。

输出
对每个查询操作,输出被查询的边的负载。

样例输入
8 6
A 2 3
A 3 4
A 3 8
A 8 7
A 6 5
Q 3 8

样例输出
6

数据规模
对于所有数据,1≤N,Q≤100000

SOL
线段树合并+启发式合并
维护子树SIZE,并查集维护连通性
答案等于 (SIZ[root]-query(in[x],out[x]))*query(in[x],out[x])
其中root为查询的边所在连通块的根,in[x],out[x]表示dfs序(x要取深度较大的那个)
不知道为什么用fread会挂……

代码:

#include<bits/stdc++.h>
using namespace std;
#define re register
#define int long long
inline int rd(){
	int re data=0;static char ch=0;ch=getchar();
	while(!isdigit(ch))ch=getchar();
	while(isdigit(ch))data=(data<<1)+(data<<3)+(ch^48),ch=getchar();
	return data;
}
inline void write(int x){if(x>9)write(x/10);putchar(x%10+'0');}
const int N=1e5+1;
int n,Q,cnt,first[N],dfn,dep[N],sz,in[N],out[N],siz[N<<4],f[N],rt[N<<4],lc[N<<4],rc[N<<4];
inline int find(int x){return f[x]==x?x:f[x]=find(f[x]);}
struct edge{int v,nxt;}e[N<<1];
inline void add(int u,int v){e[++cnt]=(edge){v,first[u]},first[u]=cnt;}
inline void insert(int &p,int l,int r,int x){
	if(!p)p=++sz;
	if(l==r){siz[p]=1;return;}
	int re mid=l+r>>1;
	if(x<=mid)insert(lc[p],l,mid,x);else insert(rc[p],mid+1,r,x);
	siz[p]=siz[lc[p]]+siz[rc[p]];
}
inline void dfs(int u,int fa){
	in[u]=++dfn,insert(rt[u],1,n,in[u]);
	for(int re i=first[u];i;i=e[i].nxt){
		int re v=e[i].v;
		if(v==fa)continue;
		dep[v]=dep[u]+1,dfs(v,u);
	}out[u]=dfn;
}
inline int merge(int p1,int p2){
	if(!p1||!p2)return p1+p2;
	siz[p1]+=siz[p2];
	lc[p1]=merge(lc[p1],lc[p2]);
	rc[p1]=merge(rc[p1],rc[p2]);
	return p1;
}
inline int query(int p,int ql,int qr,int l,int r){
	if(!p)return 0;
	if(ql<=l&&r<=qr)return siz[p];
	int re mid=l+r>>1,ret=0;
	if(ql<=mid)ret+=query(lc[p],ql,qr,l,mid);
	if(qr>mid)ret+=query(rc[p],ql,qr,mid+1,r);
	return ret;
}
struct question{int op,x,y;}q[N];
signed main(){
	n=rd(),Q=rd();
	for(int re i=1;i<=Q;i++){
		char re ch;scanf("\n%c",&ch),q[i].x=rd(),q[i].y=rd();
		if(ch=='A')q[i].op=0,add(q[i].x,q[i].y),add(q[i].y,q[i].x);else q[i].op=1;
	}
	for(int re i=1;i<=n;i++){f[i]=i;if(!in[i])dfs(i,0);}
	for(int re i=1;i<=Q;i++){
		if(!q[i].op){
			int re x=find(q[i].x),y=find(q[i].y);
			if(dep[x]>dep[y])swap(x,y);
			rt[x]=merge(rt[x],rt[y]),f[y]=x;
		}
		else{
			int re x=q[i].x,y=q[i].y;
			if(dep[x]<dep[y])swap(x,y);
			int re ff=find(x),num=query(rt[ff],in[x],out[x],1,n);
			write(num*(siz[rt[ff]]-num)),putchar('\n');			
		}
	}
	exit(0);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值