bzoj 4530: [Bjoi2014]大融合 (并查集+树链剖分+线段树)

4530: [Bjoi2014]大融合

Time Limit: 10 Sec   Memory Limit: 256 MB
Submit: 140   Solved: 83
[ Submit][ Status][ Discuss]

Description

小强要在N个孤立的星球上建立起一套通信系统。这套通信系统就是连接N个点的一个树。
这个树的边是一条一条添加上去的。在某个时刻,一条边的负载就是它所在的当前能够
联通的树上路过它的简单路径的数量。
bzoj 4530: [Bjoi2014]大融合 (并查集+树链剖分+线段树)_第1张图片
例如,在上图中,现在一共有了5条边。其中,(3,8)这条边的负载是6,因
为有六条简单路径2-3-8,2-3-8-7,3-8,3-8-7,4-3-8,4-3-8-7路过了(3,8)。
现在,你的任务就是随着边的添加,动态的回答小强对于某些边的负载的
询问。

Input

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

Output

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

Sample Input

8 6
A 2 3
A 3 4
A 3 8
A 8 7
A 6 5
Q 3 8

Sample Output

6

HINT

Source

鸣谢佚名上传

[ Submit][ Status][ Discuss]

题解:并查集+树链剖分+线段树

要求经过一条边的简单路径的数量,答案就是两个点不通过该边所能联通的点数的乘积。

那么如何维护呢?我们先将所有的边读进来然后建树,将所有散的树枝都连接到1节点上,然后进行树链剖分。初始时所有点的子树的size都是1,如果连接一条边,就将深度较浅的点到根路径上最近的断开的点之间的区间加上深度较深的点的size。用并查集维护联通块中最浅的点。查询答案的时候用(连通块的size-较深点的size)*较深点的size即可。

#include
#include
#include
#include
#include
#define N 100003
#define LL long long
using namespace std;
int n,m,tr[N*4],delta[N*4];
int point[N*2],next[N*2],v[N*2],l[N],r[N],deep[N];
int belong[N],cnt,sz,tot,pos[N],fa[N],top[N],size[N],son[N];
struct data
{
	int x,y,opt;
}a[N];
void add(int x,int y)
{
	//cout<mid) qjchange(now<<1|1,mid+1,r,ll,rr,x);
}
int pointval(int now,int l,int r,int x)
{
	if (l==r) return tr[now];
	int mid=(l+r)/2;
	pushdown(now,l,r);
	if (x<=mid) return pointval(now<<1,l,mid,x);
	else return pointval(now<<1|1,mid+1,r,x);
}
void solve(int x,int y,int z)
{
	while (belong[x]!=belong[y])
	{
		if (deep[belong[x]]deep[y])  swap(x,y);
    qjchange(1,1,n,pos[x],pos[y],z);
}
int find(int x)
{
	if (top[x]==x) return x;
	top[x]=find(top[x]);
	return top[x];
}
int main()
{
	scanf("%d%d",&n,&m);
	for (int i=1;i<=n;i++) top[i]=i;
	for (int i=1;i<=m;i++)
	 {
	 	char s[10]; scanf("%s",s);
	 	if (s[0]=='A') {
	 		a[i].opt=1;
	 		scanf("%d%d",&a[i].x,&a[i].y);
	 		add(a[i].x,a[i].y);
	 		int r1=find(a[i].x); int r2=find(a[i].y);
	 		if (r1!=r2)
	 		 top[r2]=r1;
		 }
		else
		{
			a[i].opt=0;
			scanf("%d%d",&a[i].x,&a[i].y);
		}
	 }
	for (int i=1;i<=n;i++)
	 {
	 	int r1=find(i);  int r2=find(1);
	 	if (r1!=r2)
	 	 {
	 	 	top[r2]=r1;
	 	 	add(1,i);
		  }
	 }
	deep[1]=1;
	build(1,0); dfs(1,1);
	buildtree(1,1,n);
	//for (int i=1;i<=n;i++)  cout<deep[a[i].y])  swap(a[i].x,a[i].y);
		 	int t=find(a[i].x);
		 	int x=pointval(1,1,n,pos[t]);
		 	int y=pointval(1,1,n,pos[a[i].y]);
		 	x-=y; 
		 	printf("%lld\n",(LL)x*(LL)y);
		 }
		else
		{
			if (deep[a[i].x]>deep[a[i].y]) swap(a[i].x,a[i].y);
			int k=find(a[i].x); int t=pointval(1,1,n,pos[a[i].y]);
			solve(a[i].x,k,t);
			int k1=find(a[i].y);
			top[k1]=k;
		}
	}
}


你可能感兴趣的:(并查集,线段树,树链剖分)