【BZOJ-4530】大融合 线段树合并
4530: [Bjoi2014]大融合
Time Limit: 10 Sec Memory Limit: 256 MBSubmit: 280 Solved: 167
[Submit][Status][Discuss]
Description
小强要在N个孤立的星球上建立起一套通信系统。这套通信系统就是连接N个点的一个树。
这个树的边是一条一条添加上去的。在某个时刻,一条边的负载就是它所在的当前能够联通的树上路过它的简单路径的数量。
例如,在上图中,现在一共有了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
A 2 3
A 3 4
A 3 8
A 8 7
A 6 5
Q 3 8
Sample Output
6
HINT
Source
Solution
这题的思路还是很好的,自己思考了一段时间才能想出来。
对于一次询问$<u,v>$,答案显然就是$size[u]*size[v]$,但是需要维护这样的树的形态并且询问。
然后我想了一种线段树合并的方法,但是蛋疼的地方是询问时的$size$很鸡肋,不能直接询问。
因为合并时是合并到一个点上,其余的点的线段树形态并不完整,这个地方其实和并查集很类似,那么再用并查集维护一下每个块的代表元素即可。
这样查询另一个点时也会有问题,那么限定查询区间为dfs序两端即可,然后这个题就很简单了,然后这个问题就转化成了$(size[root]-size[u])*size[u]$。
Code
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<cmath> using namespace std; inline int read() { int x=0,f=1; char ch=getchar(); while (ch<'0' || ch>'9') {if (ch=='-') f=-1; ch=getchar();} while (ch>='0' && ch<='9') {x=x*10+ch-'0'; ch=getchar();} return x*f; } #define MAXN 200010 int N,Q; struct EdgeNode{ int next,to; }edge[MAXN]; int head[MAXN],cnt=1; inline void AddEdge(int u,int v) {cnt++; edge[cnt].next=head[u]; head[u]=cnt; edge[cnt].to=v;} inline void InsertEdge(int u,int v) {AddEdge(u,v); AddEdge(v,u);} int fa[MAXN]; inline int F(int x) {if (fa[x]==x) return x; else return fa[x]=F(fa[x]);} struct SgtNode{ int lson,rson,size; }tree[MAXN*20]; int root[MAXN],sz; inline void Update(int x) {tree[x].size=tree[tree[x].lson].size+tree[tree[x].rson].size;} inline int Merge(int x,int y) { if (!x || !y) return x|y; tree[x].size+=tree[y].size; tree[x].lson=Merge(tree[x].lson,tree[y].lson); tree[x].rson=Merge(tree[x].rson,tree[y].rson); return x; } inline void Insert(int &x,int l,int r,int pos) { x=++sz; if (l==r) { tree[x].size=1; return; } int mid=(l+r)>>1; if (pos<=mid) Insert(tree[x].lson,l,mid,pos); else Insert(tree[x].rson,mid+1,r,pos); Update(x); } inline int Query(int x,int l,int r,int L,int R) { if (!x) return 0; if (L<=l && R>=r) return tree[x].size; int mid=(l+r)>>1,re=0; if (L<=mid) re+=Query(tree[x].lson,l,mid,L,R); if (R>mid) re+=Query(tree[x].rson,mid+1,r,L,R); return re; } int pl[MAXN],pre[MAXN],pr[MAXN],dfn,deep[MAXN]; inline void DFS(int now,int last) { pl[now]=++dfn; pre[dfn]=now; for (int i=head[now]; i; i=edge[i].next) if (edge[i].to!=last) deep[edge[i].to]=deep[now]+1, DFS(edge[i].to,now); pr[now]=dfn; } struct QNode{ int opt,x,y; }Qr[MAXN]; int main() { N=read(),Q=read(); for (int i=1; i<=Q; i++) { char opt[5]; scanf("%s",opt+1); int x=read(),y=read(); if (opt[1]=='A') InsertEdge(x,y); Qr[i]=(QNode){(opt[1]=='A'? 0:1),x,y}; } for (int i=1; i<=N; i++) if (!deep[i]) DFS(i,0); for (int i=1; i<=N; i++) fa[i]=i,Insert(root[i],1,N,pl[i]); for (int i=1; i<=Q; i++) { if (Qr[i].opt==0) { int x=F(Qr[i].x),y=F(Qr[i].y); if (deep[x]>deep[y]) swap(x,y); root[x]=Merge(root[x],root[y]); fa[y]=x; } else { int x=Qr[i].x,y=Qr[i].y,z; if (deep[x]<deep[y]) swap(x,y); z=F(x); int siz=Query(root[z],1,N,pl[x],pr[x]); printf("%lld\n",1LL*(tree[root[z]].size-siz)*siz); } } return 0; }
——It's a lonely path. Don't make it any lonelier than it has to be.