【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

鸣谢佚名上传

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;
}

  

转载于:https://www.cnblogs.com/DaD3zZ-Beyonder/p/6581079.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值