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

  

【BZOJ-4530】大融合 线段树合并的更多相关文章

  1. BZOJ.3545.[ONTAK2010]Peaks(线段树合并)

    题目链接 \(Description\) 有n个座山,其高度为hi.有m条带权双向边连接某些山.多次询问,每次询问从v出发 只经过边权<=x的边 所能到达的山中,第K高的是多少. \(Solut ...

  2. bzoj 4631: 踩气球 线段树合并

    4631: 踩气球 Time Limit: 10 Sec  Memory Limit: 256 MBSubmit: 265  Solved: 136[Submit][Status][Discuss] ...

  3. BZOJ:5457: 城市(线段树合并)(尚待优化)

    5457: 城市 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 18  Solved: 12[Submit][Status][Discuss] Des ...

  4. 洛谷 4219/BZOJ 4530 大融合

    4530: [Bjoi2014]大融合 Time Limit: 10 Sec  Memory Limit: 256 MBSubmit: 990  Solved: 604[Submit][Status] ...

  5. bzoj 4756 Promotion Counting —— 线段树合并

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=4756 合并子树的权值线段树: merge 返回 int 或者是 void 都可以. 代码如下 ...

  6. [BZOI2014]大融合——————线段树进阶

    竟然改了不到一小时就改出来了, 可喜可贺 Description Solution 一开始想的是边两侧简单路径之和的乘积,之后发现这是个树形结构,简单路径数就是节点数. 之后的难点就变成了如何求线段树 ...

  7. bzoj 4530 大融合 —— LCT维护子树信息

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=4530 用LCT维护子树 size,就是实边和虚边分开维护: 看博客:https://blog ...

  8. BZOJ4530 BJOI2014大融合(线段树合并+并查集+dfs序)

    易知所求的是两棵子树大小的乘积.先建出最后所得到的树,求出dfs序和子树大小.之后考虑如何在动态加边过程中维护子树大小.这个可以用树剖比较简单的实现,但还有一种更快更优美的做法就是线段树合并.对每个点 ...

  9. 2019.01.14 bzoj4530: [Bjoi2014]大融合(线段树合并)

    传送门 线段树合并菜题. 题意简述:nnn个点,支持连边以及查询一个点所在连通块中经过这个点的路径条数,保证这张图时刻为森林. 思路: 先建出所有操作完之后的树统计出dfsdfsdfs序 注意有可能是 ...

随机推荐

  1. 何凯文每日一句打卡||DAY5

  2. Ionic -- css

    Header 固定在头部,可以包含标题标签,可以有左右按钮 样式:bar bar-header bar-light 第一个小节 第二个表示的是头部 第三个表示颜色 子头部,需要在ion-content ...

  3. 20155325 2016-2017-2 《Java程序设计》第8周学习总结

    教材学习内容总结 NIO用于高级输入/输出处理 名称 衔接数据源与目的地 IO InputStream,OutputStream NIO Channel 类 方法 方法的作用 ReadableByte ...

  4. Android改进版CoverFlow效果控件

    最近研究了一下如何在Android上实现CoverFlow效果的控件,其实早在2010年,就有Neil Davies开发并开源出了这个控件,Neil大神的这篇博客地址http://www.inter- ...

  5. [CEOI2015 Day2]世界冰球锦标赛 (双向搜索)

    题目描述 [CEOI2015 Day2]世界冰球锦标赛译自 CEOI2015 Day2 T1「Ice Hockey World Championship」 今年的世界冰球锦标赛在捷克举行.Bobek ...

  6. 克隆虚拟机重启之后eth0不见的解决方案

    今天用虚拟机克隆多一个虚拟机的时候,发现克隆之后的新虚拟机的网卡eth0在配置之后完全是用不了的,下面说一下我的解决办法,亲测可用. 1.用ipconfig命令查看ip信息的时候会发现虚拟机没有找到e ...

  7. net MongoDB安装

    Mongo服务器端下载链接:https://www.mongodb.com/download-center?jmp=nav 客户端查看工具Mongovue工具下载链接:http://pan.baidu ...

  8. Android 7.0 新增功能和api

    Android 7.0 Nougat 为用户和开发者引入多种新功能.本文重点介绍面向开发者的新功能. 请务必查阅 Android 7.0 行为变更以了解平台变更可能影响您的应用的领域. 要详细了解 A ...

  9. Android安全系列之:如何在native层保存关键信息

    相信大家在日常开发中都要安全层面的需求,最典型的莫过于加密.而apk是脆弱的,反编译拿到你的源码轻而易举,这时候我们就需要更保险的手段来保存密钥之类的关键信息.本文就细致地讲解简单却实用的native ...

  10. SqlServer 批量备份

    -- 实现方式1:使用游标 DECLARE @FileName VARCHAR(200), @CurrentTime VARCHAR(50), @DBName VARCHAR(100), @SQL V ...