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. bzoj千题计划293:bzoj3142: [Hnoi2013]数列

    http://www.lydsy.com/JudgeOnline/problem.php?id=3142 如果已知数列的差分数列a[1]~a[k-1] 那么这种差分方式对答案的贡献为 N-Σ a[i] ...

  2. 流媒体技术学习笔记之(十二)Linux(Ubuntu)环境运行EasyDarwin

    Debug问题??? ./easydarwin -c ./easydarwin.xml & //这样的话是80端口 ./easydarwin -c ./easydarwin.xml -d // ...

  3. python学习笔记7-网络编程

    import urllib.request import json,requests #urlib模块,不常用 url = 'http://api.nnzhp.cn/api/user/stu_info ...

  4. C++ 修饰符类型

    C++ 修饰符类型 C++ 允许在 char.int 和 double 数据类型前放置修饰符.修饰符用于改变基本类型的含义,所以它更能满足各种情境的需求. 下面列出了数据类型修饰符: signed u ...

  5. caoha

  6. 洛谷 P4656: LOJ 2484: [CEOI2017]Palindromic Partitions

    菜菜只能靠写简单字符串哈希维持生活. 题目传送门:LOJ #2484. 题意简述: 题面讲得很清楚了. 题解: 很显然从两边往中间推,能选的就选上这个贪心策略是对的. 如何判断能不能选上,直接字符串哈 ...

  7. mysql学习------二进制日志

    一.什么是二进制日志 1.记录对数据发生或潜在发生更改的sql语句 2.二进制格式保存 3.用途广泛,包括 a.查看数据库变更历史 b.数据库增量备份 c.数据库灾难恢复 d.mysql replic ...

  8. Coursera台大机器学习技法课程笔记14-Radial Basis Function Network

    将Radial Basis Function与Network相结合.实际上衡量两个点的相似性:距离越近,值越大. 将神经元换为与距离有关的函数,就是RBF Network: 可以用kernel和RBF ...

  9. Fiddler 常用功能总结

    1.fiddler相关配置   2.如何抓包 移动端 ①保持手机和电脑处于同一网络中 ②设置手机的代理为电脑当前所处网络的IP,端口号为:8888,eg:10.12.1.64:8888. ③ 启动ap ...

  10. AWS 使用经验

    掐指一算,自己第一次使用 AWS 已经是两年前的事情了,这也是云计算和大数据等技术迅猛发展的两年.这期间,大抵间间断断地使用着,FreeTier Instance 也运行快一年了,马上进入收费周期.虽 ...