【BZOJ-4530】大融合 線段樹合併

4530: [Bjoi2014]大融合

Time Limit: 10 Sec  Memory Limit: 256 MB
Submit: 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

Sample Output

6

HINT

Source

鳴謝佚名上傳php

Solution

這題的思路仍是很好的,本身思考了一段時間才能想出來。ios

對於一次詢問$<u,v>$,答案顯然就是$size[u]*size[v]$,可是須要維護這樣的樹的形態而且詢問。spa

而後我想了一種線段樹合併的方法,可是蛋疼的地方是詢問時的$size$很雞肋,不能直接詢問。blog

由於合併時是合併到一個點上,其他的點的線段樹形態並不完整,這個地方其實和並查集很相似,那麼再用並查集維護一下每一個塊的表明元素便可。ip

這樣查詢另外一個點時也會有問題,那麼限定查詢區間爲dfs序兩端便可,而後這個題就很簡單了,而後這個問題就轉化成了$(size[root]-size[u])*size[u]$。get

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;
}
相關文章
相關標籤/搜索