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之间有一条边。

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

Data Constraint

对于40%的数据,N,Q≤1000

对于100%的数据,1≤N,Q≤100000

Solution

  • 这题的思路很巧妙。考虑树链剖分和并查集。

  • 先把边都连上,形成一些森林,处理树链剖分的有关信息。

  • 接着再逐个连边,把深度高的点设为x,深度低的点设为y,y所在的链链顶为z。

  • 再设x的子树大小为m1,z的子树大小为m2,有如下情况:

  • ①:连边操作,从y到z的子树大小全部加上x的子树大小(用线段树区间加实现)

  • ②:查询操作,则答案即为m1*(m2-m1)。

Code

#include<cstdio>
#include<algorithm>
using namespace std;
const int N=100001;
struct data
{bool z;int x,y;
}a[N];
struct segment
{int l,r,sum,c;
}g[N<<2];
int tot,num;
int first[N],next[N<<1],en[N<<1];
int fa[N],size[N],dep[N];
int top[N],son[N],tree[N],pre[N];
int f[N],t[N],h[N];
inline int read()
{int X=0,w=1; char ch=0;while(ch<'0' || ch>'9') {if(ch=='-') w=-1;ch=getchar();}while(ch>='0' && ch<='9') X=(X<<3)+(X<<1)+ch-'0',ch=getchar();return X*w;
}
inline void insert(int x,int y)
{next[++tot]=first[x];first[x]=tot;en[tot]=y;
}
inline int get(int x)
{if(f[x]==x) return x;return f[x]=get(f[x]);
}
inline void dfs1(int x)
{dep[x]=dep[fa[x]]+1;size[x]=1;for(int i=first[x];i;i=next[i])if(en[i]!=fa[x]){fa[en[i]]=x;dfs1(en[i]);size[x]+=size[en[i]];if(!son[x] || size[son[x]]<size[en[i]]) son[x]=en[i];}
}
inline void dfs2(int x,int y)
{top[pre[tree[x]=++num]=x]=y;if(!son[x]) return;dfs2(son[x],y);for(int i=first[x];i;i=next[i])if(en[i]!=fa[x] && en[i]!=son[x]) dfs2(en[i],en[i]);
}
inline void update(int v)
{int ls=v<<1,rs=ls|1;if(g[v].c){g[ls].sum+=g[v].c;g[ls].c+=g[v].c;g[rs].sum+=g[v].c;g[rs].c+=g[v].c;g[v].c=0;}
}
inline void make(int v,int l,int r)
{g[v].l=l,g[v].r=r;if(l==r){g[v].sum=1;return;}int mid=(l+r)>>1;make(v<<1,l,mid);make(v<<1|1,mid+1,r);g[v].sum=g[v<<1].sum+g[v<<1|1].sum+1;
}
inline void change(int v,int x,int y,int z)
{if(g[v].l==x && g[v].r==y){g[v].sum+=z;g[v].c+=z;return;}update(v);int mid=(g[v].l+g[v].r)>>1;if(y<=mid) change(v<<1,x,y,z); elseif(x>mid) change(v<<1|1,x,y,z); else{change(v<<1,x,mid,z);change(v<<1|1,mid+1,y,z);}
}
inline int find(int v,int x)
{if(g[v].l==g[v].r) return g[v].sum;update(v);int mid=(g[v].l+g[v].r)>>1;if(x<=mid) return find(v<<1,x);return find(v<<1|1,x);
}
int main()
{int n=read(),q=read();for(int i=1;i<=q;i++){char ch=getchar();while(ch!='A' && ch!='Q') ch=getchar();a[i].x=read(),a[i].y=read();if(a[i].z=ch=='A'){insert(a[i].x,a[i].y);insert(a[i].y,a[i].x);}}for(int i=1;i<=n;i++)if(!size[i]){dfs1(i);dfs2(i,i);}make(1,1,n);for(int i=1;i<=n;i++) h[t[f[i]=i]=i]=1;for(int i=1;i<=q;i++){int x=a[i].x,y=a[i].y;if(dep[x]<dep[y]) swap(x,y);int f1=get(x),f2=get(y);if(a[i].z){int n1=y,n2=t[f2];int m1=top[n1],m2=top[n2];if(dep[m1]<dep[t[f2]]) m1=t[f2];if(dep[m2]<dep[t[f2]]) m2=t[f2];while(m1!=m2){if(dep[m1]<dep[m2]) swap(m1,m2),swap(n1,n2);change(1,tree[m1],tree[n1],h[f1]);n1=fa[m1],m1=top[n1];if(dep[m1]<dep[t[f2]]) m1=t[f2];}if(dep[n1]>dep[n2]) swap(n1,n2);change(1,tree[n1],tree[n2],h[f1]);h[f2]+=h[f1];if(dep[t[f1]]<dep[t[f2]]) t[f2]=t[f1];f[f1]=f2;}else{int m1=find(1,tree[x]),m2=find(1,tree[t[f2]])-m1;printf("%lld\n",(long long)m1*m2);}}return 0;
}

方法二

  • 显然这道题也可以用可维护子树信息的LCT。

  • 那么 LCT 如何维护子树信息呢?(以维护子树大小为例)

  • 我们可以多开一个数组记录一个点虚树上的大小信息,合并时直接并到该点上。

  • 我们发现只有当 Access 或 Link 操作时会影响到信息记录(其他操作都不会影响)。

  • 当 Access 时,只需将虚树信息加上原右儿子的 Size ,再减去新右儿子的 Size 即可。

  • 而当 Link 时,相当于 xxx 向 y" role="presentation" style="position: relative;">yyy 添一条虚边,

  • 于是除了 makeroot(x) 外,再执行一遍 makeroot(y),(消除修改 yyy 对其子树各节点信息的影响)

  • 最后再将 y" role="presentation" style="position: relative;">yyy 虚树信息加上 xxx 的 Size 即可。

  • 时间复杂度 O(N log N)" role="presentation" style="position: relative;">O(N log N)O(N log N)O(N\ log\ N) 。

Code2

#include<cstdio>
#include<algorithm>
#include<cctype>
using namespace std;
const int N=1e5+5;
int top;
int fa[N],size[N],s[N][2],g[N],st[N];
bool rev[N];
inline int read()
{int X=0,w=0; char ch=0;while(!isdigit(ch)) w|=ch=='-',ch=getchar();while(isdigit(ch)) X=(X<<3)+(X<<1)+(ch^48),ch=getchar();return w?-X:X;
}
inline bool pd(int x)
{return x==s[fa[x]][1];
}
inline bool isroot(int x)
{return x^s[fa[x]][0] && x^s[fa[x]][1];
}
inline void reverse(int x)
{if(x) swap(s[x][0],s[x][1]),rev[x]^=1;
}
inline void update(int x)
{size[x]=size[s[x][0]]+size[s[x][1]]+1+g[x];
}
inline void down(int x)
{if(rev[x]){reverse(s[x][0]),reverse(s[x][1]);rev[x]=false;}
}
inline void rotate(int x)
{int y=fa[x],w=pd(x);if(s[y][w]=s[x][w^1]) fa[s[y][w]]=y;if((fa[x]=fa[y]) && !isroot(y)) s[fa[y]][pd(y)]=x;s[fa[y]=x][w^1]=y;update(y);
}
inline void splay(int x)
{for(int y=st[top=1]=x;!isroot(y);y=fa[y]) st[++top]=fa[y];while(top) down(st[top--]);for(int y;!isroot(x);rotate(x))if(!isroot(y=fa[x])) rotate(pd(x)==pd(y)?y:x);update(x);
}
inline void access(int x)
{for(int y=0;x;x=fa[y=x]){splay(x);g[x]+=size[s[x][1]];g[x]-=size[s[x][1]=y];update(x);}
}
inline void mkroot(int x)
{access(x),splay(x),reverse(x);
}
inline void link(int x,int y)
{mkroot(x),mkroot(y);g[fa[x]=y]+=size[x];update(y);
}
int main()
{int n=read(),m=read();for(int i=1;i<=n;i++) size[i]=1;while(m--){char ch=getchar();int x=read(),y=read();if(ch=='A') link(x,y); else{mkroot(x),access(y),splay(y);printf("%lld\n",(long long)size[x]*(size[y]-size[x]));}}return 0;
}

JZOJ 3766. 【BJOI2014】大融合相关推荐

  1. [Bjoi2014]大融合

    [Bjoi2014]大融合 Time Limit: 10 Sec  Memory Limit: 256 MB Submit: 394  Solved: 244 [Submit][Status][Dis ...

  2. [BZOJ4530][Bjoi2014]大融合 LCT + 启发式合并

    [BZOJ4530][Bjoi2014]大融合 试题描述 小强要在N个孤立的星球上建立起一套通信系统.这套通信系统就是连接N个点的一个树. 这个树的边是一条一条添加上去的.在某个时刻,一条边的负载就是 ...

  3. LCT维护子树信息(BZOJ4530:[BJOI2014]大融合)

    题面 没有权限号的可以去LOJ Sol 大家都知道,\(LCT\)上有许多实边和虚边 实边就是每棵\(Splay\)上的既认父亲又认儿子的边 虚边就是\(Splay\)和\(Splay\)之间只认父亲 ...

  4. BZOJ4530:[BJOI2014]大融合

    Description 小强要在N个孤立的星球上建立起一套通信系统.这套通信系统就是连接N个点的一个树. 这个树的边是一条一条添加上去的.在某个时刻,一条边的负载就是它所在的当前能够 联通的树上路过它 ...

  5. bzoj 4530 [Bjoi2014]大融合——LCT维护子树信息

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=4530 LCT维护子树 siz .设 sm[ ] 表示轻儿子的 siz 和+1(1是自己的si ...

  6. LOJ#2230. 「BJOI2014」大融合

    LOJ#2230. 「BJOI2014」大融合 题目描述 小强要在$N$个孤立的星球上建立起一套通信系统.这套通信系统就是连接$N$个点的一个树.这个树的边是一条一条添加上去的. 在某个时刻,一条边的 ...

  7. Vue笔记大融合总结

    <!DOCTYPE html> <html lang="en"><head><meta charset="UTF-8" ...

  8. 极客日报:阿里再度调整组织架构:天猫淘宝大融合,新设三大中心;苹果M1首席芯片设计师跳槽至英特尔

    一分钟速览新闻点! 腾讯接入数字人民币,微信支持数字人民币支付 阿里再度调整组织架构:天猫淘宝大融合,新设三大中心 七成清华毕业生进入体制内就业 北京最受欢迎:华为腾讯等科技企业也受青睐 钉钉近期将推 ...

  9. 重塑大融合体系,立体推进“业财合一”

    3月16日,主题为"智能会计 价值财务"的2023企业数智化财务创新峰会在北京召开.用友网络高级专家张伟发表了题为"聚势向新:智能会计助力企业高质量发展"的演讲 ...

  10. 全域旅游发展的“首都经验” , 全域旅游的十大融合

    一.北京发展全域旅游的历史渊源和现实基础 北京是全域旅游思想形成和理论创新的重要发源地之一,2008年延庆的千家店镇就提出"镇景合一",2011年延庆在全国率先提出建设" ...

最新文章

  1. 搭建 VUE + NODE.JS + ElementUI 学习过程中问题总结
  2. SQL Try Catch
  3. java final 变量 回收_在Java中将final用于变量会改善垃圾回收吗?
  4. 获得Google搜索字符串中的关键字
  5. easyui收派标准客户端表单校验
  6. 世上最伟大的十个公式,质能方程排名第五
  7. iOS 归档 解档使用总结
  8. 从零开发微信公众号(PC)
  9. C#版OPOS打印(基于北洋OPOS SDK二次开发包,支持EPSON和北洋、佳博、商祺等支持标准ESC/POS指令的POS打印机)...
  10. 软件工程师必备的简历模板
  11. python 结构体数组的正确初始化方法
  12. 为什么要使用Tomcat?
  13. 漫画戏说:如何破解MD5加密算法
  14. 【毕业论文写作】毕业论文任务书怎么写?
  15. 3500字干货 | 大家都在谈的数据思维,到底要怎么建立?
  16. ❤️数据结构之栈(图文版详解)❤️
  17. 中图法分类号(计算机专业)
  18. Docker三剑客之Docker compose多容器管理(nginx+haproxy实现负载均衡)
  19. arduino tft 方向_怎样将TFT 1.44与Arduino Nano结合使用
  20. 侦测IE的代理服务器

热门文章

  1. 请解释和、|和||的区别?
  2. C++的三种容器适配器
  3. 1.2传值、引用、常量引用
  4. [ 云炬创业基础笔记]商业模式创新
  5. 科大星云诗社动态20210207
  6. 科大星云诗社动态20210827
  7. python可以开多少线程_Python开启线程,在函数中开线程的实例
  8. 形变立体跟踪-基于稠密运动估计和力学仿真(2)
  9. VTK修炼之道11:基本数据结构_数据对象数据集
  10. Delphi指针大全