点此看题面

大致题意: 给你两棵\(n\)个点的树,对于第一棵树中的每条边\(e_1\),求存在多少条第二棵树中的边\(e_2\),使得第一棵树删掉\(e_1\)加上\(e_2\)、第二棵树删掉\(e_2\)加上\(e_1\)后皆仍为生成树。

题意转化

考虑对于\(e_1(x,y)\),合法的\(e_2(u,v)\),必然存在于第二棵树中\(x\)到\(y\)的路径之上。

同理,则\(e_1\)也应该存在于第一棵树中\(u\)到\(v\)的路径之上。

考虑到我们是在枚举\(e_1\),而每条边都可以被表示为\((x,fa_x)\)的形式。

也就是说,若\(e_1\)存在于第一棵树中\(u\)到\(v\)的路径之上,则\(u,v\)应该一个存在于\(x\)子树内,一个在\(x\)子树外。

那么题意就变成了,对于第一棵树中的每一个点\(x\),设其在第一棵树中的父节点为\(fa_x\),求在第二棵树中\(x\)到\(fa_x\)路径存在多少条边\((u,v)\),满足\(u,v\)一个存在于\(x\)子树内,一个在\(x\)子树外。

树链剖分+树上启发式合并

考虑对第二棵树进行树链剖分,对一棵树进行树上启发式合并

则我们处理每一个\(x\)时,此时第二棵树的树剖线段树上,第一棵树中\(x\)子树内点为\(1\),\(x\)子树外点为\(0\)。

然后我们可以树剖抠出第二棵树中的\(x\)到其第一棵树中父亲的路径,然后求出有多少对相邻的\(01\)。

合并两个区间时,我们只要记录每个区间的答案及其左右端值,然后比较左区间右端和右区间左端是否相同即可。

注意树剖抠出路径的时候有些细节需要注意,对于两个点向上跳的过程要分别进行统计,且最后合并两个点向上跳区间时要先将其中一个点左右端值交换一下再合并。

代码

  1. #include<bits/stdc++.h>
  2. #define Tp template<typename Ty>
  3. #define Ts template<typename Ty,typename... Ar>
  4. #define Reg register
  5. #define RI Reg int
  6. #define Con const
  7. #define CI Con int&
  8. #define I inline
  9. #define W while
  10. #define N 200000
  11. #define LL long long
  12. #define RL Reg LL
  13. #define CL Con LL&
  14. #define add(x,y) (e[++ee].nxt=lnk[x],e[lnk[x]=ee].to=y)
  15. #define swap(x,y) (x^=y^=x^=y)
  16. using namespace std;
  17. int n;struct edge {int to,nxt;};
  18. class FastIO
  19. {
  20. private:
  21. #define FS 100000
  22. #define tc() (A==B&&(B=(A=FI)+fread(FI,1,FS,stdin),A==B)?EOF:*A++)
  23. #define pc(c) (C==E&&(clear(),0),*C++=c)
  24. #define tn (x<<3)+(x<<1)
  25. #define D isdigit(c=tc())
  26. int T;char c,*A,*B,*C,*E,FI[FS],FO[FS],S[FS];
  27. public:
  28. I FastIO() {A=B=FI,C=FO,E=FO+FS;}
  29. Tp I void read(Ty& x) {x=0;W(!D);W(x=tn+(c&15),D);}
  30. Ts I void read(Ty& x,Ar&... y) {read(x),read(y...);}
  31. Tp I void write(Ty x) {W(S[++T]=x%10+48,x/=10);W(T) pc(S[T--]);}
  32. Tp I void write(Con Ty& x,Con char& y) {write(x),pc(y);}
  33. I void clear() {fwrite(FO,1,C-FO,stdout),C=FO;}
  34. }F;
  35. class Tree1//题意中的第二棵树,树链剖分
  36. {
  37. private:
  38. int ee,d,lnk[N+5],Sz[N+5],son[N+5],fa[N+5],dep[N+5],dfn[N+5],Top[N+5];edge e[N<<1];
  39. struct Il//区间
  40. {
  41. int L,R,S;I Il(CI x=0,CI y=0,CI v=0):L(x),R(y),S(v){}
  42. I Il operator + (Con Il& o) {return ~S?(~o.S?Il(L,o.R,S+o.S+(R^o.L)):*this):o;}//合并区间
  43. };
  44. template<int SZ> class SegmentTree//树剖线段树
  45. {
  46. private:
  47. #define LT l,mid,rt<<1
  48. #define RT mid+1,r,rt<<1|1
  49. #define PU(x) (O[x]=O[x<<1]+O[x<<1|1])
  50. int n;Il O[SZ<<2];
  51. I void Upt(CI x,CI v,CI l,CI r,CI rt)//单点修改
  52. {
  53. if(l==r) return (void)(O[rt].L=O[rt].R=v);RI mid=l+r>>1;
  54. x<=mid?Upt(x,v,LT):Upt(x,v,RT),PU(rt);
  55. }
  56. I Il Qry(CI tl,CI tr,CI l,CI r,CI rt)//扣区间
  57. {
  58. if(tl==l&&r==tr) return O[rt];RI mid=l+r>>1;
  59. if(tr<=mid) return Qry(tl,tr,LT);if(tl>mid) return Qry(tl,tr,RT);
  60. return Qry(tl,mid,LT)+Qry(mid+1,tr,RT);
  61. }
  62. public:
  63. I void Build(CI _n) {n=_n;}I void Upt(CI x,CI v) {Upt(x,v,1,n,1);}
  64. I Il Qry(CI l,CI r) {return Qry(l,r,1,n,1);}
  65. };SegmentTree<N> S;
  66. I void dfs1(CI x)//第一遍树剖dfs
  67. {
  68. for(RI i=(Sz[x]=1,son[x]=0,lnk[x]);i;i=e[i].nxt) e[i].to^fa[x]&&
  69. (
  70. dep[e[i].to]=dep[fa[e[i].to]=x]+1,dfs1(e[i].to),
  71. Sz[x]+=Sz[e[i].to],Sz[e[i].to]>Sz[son[x]]&&(son[x]=e[i].to)
  72. );
  73. }
  74. I void dfs2(CI x,CI col)//第二遍树剖dfs
  75. {
  76. Top[x]=col,dfn[x]=++d,son[x]&&(dfs2(son[x],col),0);
  77. for(RI i=lnk[x];i;i=e[i].nxt) e[i].to^fa[x]&&
  78. e[i].to^son[x]&&(dfs2(e[i].to,e[i].to),0);
  79. }
  80. public:
  81. I void Clear() {memset(lnk,0,sizeof(lnk)),ee=d=0;}I void Add(CI x,CI y) {add(x,y);}
  82. I void TreeChainDissection() {dfs1(1),dfs2(1,1),S.Build(n);}
  83. I void Upt(CI x,CI v) {S.Upt(dfn[x],v);}//单点修改
  84. I int Qry(RI x,RI y)//扣区间
  85. {
  86. Il tl,tr;tl.S=tr.S=-1;W(Top[x]^Top[y])
  87. {
  88. if(dep[Top[x]]>dep[Top[y]]) tl=S.Qry(dfn[Top[x]],dfn[x])+tl,x=fa[Top[x]];//记录第一个点向上跳的过程
  89. else tr=S.Qry(dfn[Top[y]],dfn[y])+tr,y=fa[Top[y]];//记录第二个点向上跳的过程
  90. }
  91. dfn[x]>dfn[y]?(tl=S.Qry(dfn[y],dfn[x])+tl):(tr=S.Qry(dfn[x],dfn[y])+tr);//判断是哪个点向上跳
  92. return swap(tl.L,tl.R),(tl+tr).S;//交换第一个点区间左右端值,然后合并
  93. }
  94. }T1;
  95. class Tree2//题意中的第一棵树,树上启发式合并
  96. {
  97. private:
  98. int ee,lnk[N+5],Sz[N+5],son[N+5],ans[N+5];edge e[N<<1];
  99. I void Fill(CI x,CI lst,CI v)//把整棵子树染成v
  100. {
  101. T1.Upt(x,v);for(RI i=lnk[x];i;i=e[i].nxt) e[i].to^lst&&(Fill(e[i].to,x,v),0);
  102. }
  103. I void dfs(CI x,CI lst,CI pos)//遍历
  104. {
  105. if(son[x])//如果有子节点
  106. {
  107. RI i,p;for(i=lnk[x];i;i=e[i].nxt) e[i].to^lst&&//处理轻儿子
  108. (e[i].to^son[x]?(dfs(e[i].to,x,i+1>>1),Fill(e[i].to,x,0),0):p=i+1>>1);//处理完就染白
  109. for(dfs(son[x],x,p),i=lnk[x];i;i=e[i].nxt)//处理重儿子
  110. e[i].to^lst&&e[i].to^son[x]&&(Fill(e[i].to,x,1),0);//然后把其他儿子重新染黑
  111. }T1.Upt(x,1),pos&&(ans[pos]=T1.Qry(x,lst));//把当前点染黑,计算答案
  112. }
  113. public:
  114. I void Clear() {memset(lnk,0,sizeof(lnk)),ee=0;}I void Add(CI x,CI y) {add(x,y);}
  115. I void Init(CI x,CI lst)//初始化求重儿子
  116. {
  117. for(RI i=(Sz[x]=1,son[x]=0,lnk[x]);i;i=e[i].nxt) e[i].to^lst&&
  118. (Init(e[i].to,x),Sz[x]+=Sz[e[i].to],Sz[e[i].to]>Sz[son[x]]&&(son[x]=e[i].to));
  119. }
  120. I void DSU_on_Tree()//树上启发式合并
  121. {
  122. dfs(1,0,0),Fill(1,0,0);//遍历,清空
  123. for(RI i=1;i^n;++i) F.write(ans[i]," \n"[i==n-1]);//输出答案
  124. }
  125. }T2;
  126. int main()
  127. {
  128. RI Tt,i,x,y;F.read(Tt);W(Tt--)
  129. {
  130. T1.Clear(),T2.Clear(),F.read(n);//清空
  131. for(i=1;i^n;++i) F.read(x,y),T2.Add(x,y),T2.Add(y,x);//读入题意中的第一棵树
  132. for(i=1;i^n;++i) F.read(x,y),T1.Add(x,y),T1.Add(y,x);//读入题意中的第二棵树
  133. T1.TreeChainDissection(),T2.Init(1,0),T2.DSU_on_Tree();//求答案
  134. }return F.clear(),0;
  135. }

【CodeChef EDGEST】Edges in Spanning Trees(树链剖分+树上启发式合并)的更多相关文章

  1. Codeforces 1009 F. Dominant Indices(长链剖分/树上启发式合并)

    F. Dominant Indices 题意: 给一颗无向树,根为1.对于每个节点,求其子树中,哪个距离下的节点数量最多.数量相同时,取较小的那个距离. 题目: 这类题一般的做法是树上的启发式合并,复 ...

  2. 【BZOJ-4568】幸运数字 树链剖分 + 线性基合并

    4568: [Scoi2016]幸运数字 Time Limit: 60 Sec  Memory Limit: 256 MBSubmit: 238  Solved: 113[Submit][Status ...

  3. HDU 5111 Alexandra and Two Trees 树链剖分 + 主席树

    题意: 给出两棵树,每棵树的节点都有一个权值. 同一棵树上的节点的权值互不相同,不同树上节点的权值可以相同. 要求回答如下询问: \(u_1 \, v_1 \, u_2 \, v_2\):询问第一棵树 ...

  4. [Codechef - ADITREE] Adi and the Tree - 树链剖分,线段树

    [Codechef - ADITREE] Adi and the Tree Description 树上每个节点有一个灯泡,开始所有灯泡都是熄灭的.每次操作给定两个数 \(a,b\) ,将 \(a,b ...

  5. BZOJ 3631 松鼠的新家 - 树链剖分 / 树上差分

    传送门 分析: 树链剖分:x->y,将x到y的路径加一,并将x端点的答案-1,最后统计答案. 树上差分:x->y,x+1,y+1,lca-1,fa[lca]-1,并将x打上标记,最后统计前 ...

  6. 【BZOJ3307】雨天的尾巴 题解(树链剖分+树上差分)

    题目链接 题目大意:给定一颗含有$n$个结点的树,每次选择两个结点$x$和$y$,对从$x$到$y$的路径上发放一带$z$类型的物品.问完成所有操作后每个结点发放最多的时哪种物品. 普通的树链剖分貌似 ...

  7. 【BZOJ-4326】运输计划 树链剖分 + 树上差分 + 二分

    4326: NOIP2015 运输计划 Time Limit: 30 Sec  Memory Limit: 128 MBSubmit: 703  Solved: 461[Submit][Status] ...

  8. hdu 5893 (树链剖分+合并)

    List wants to travel Time Limit: 6000/3000 MS (Java/Others)    Memory Limit: 131072/131072 K (Java/O ...

  9. 树链剖分学习&BZOJ1036

    题目传送门 树链剖分,计算机术语,指一种对树进行划分的算法,它先通过轻重边剖分将树分为多条链,保证每个点属于且只属于一条链,然后再通过数据结构(树状数组.SBT.SPLAY.线段树等)来维护每一条链. ...

随机推荐

  1. HttpClient之用CloseableHttpClient发送post请求

    使用HttpClient发送请求的一般步骤(1) 创建HttpClient对象.(2)创建请求方法的实例,并指定请求URL.如果需要发送GET请求,创建HttpGet对象:如果需要发送POST请求,创 ...

  2. 第04组 Alpha冲刺(3/4)

    队名:斗地组 组长博客:地址 作业博客:Alpha冲刺(3/4) 各组员情况 林涛(组长) 过去两天完成了哪些任务: 1.收集各个组员的进度 2.写博客 展示GitHub当日代码/文档签入记录: 接下 ...

  3. python-pandas读取mongodb、读取csv文件

    续上一篇博客(‘’selenium爬取NBA并将数据存储到MongoDB‘)https://www.cnblogs.com/lutt/p/10810581.html 本篇的内容是将存储到mongo的数 ...

  4. LeetCode 350: 两个数组的交集 II Intersection of Two Arrays II

    题目: 给定两个数组,编写一个函数来计算它们的交集. Given two arrays, write a function to compute their intersection. 示例 1: 输 ...

  5. 【前端知识体系-NodeJS相关】对于EventLoop(事件轮询)机制你到底了解多少?

    EventLoop 1. EventLoop的执行流程图 ┌───────────────────────┐ ┌─>│ timers │<----- 执行 setTimeout().set ...

  6. WPF自定义控件的制作

    因为有时候需要定制化的控件,需要多个控件的组合及复杂功能的集成,这样可以考虑自定义用户控件.下面分享一个简单的数值增减功能的自定义控件作为说明. 效果图如下: 1.创建自定义用户控件(添加->新 ...

  7. Delphi - 创建SuperDll 持续更新

    Delphi SuperDll 作为一名5年的Delpher,一直认为Delphi是桌面应用的王者,我相信其他的Delpher也这么认为. 但是,慢慢的我发现普通方式的Delphi开发会造成代码的严重 ...

  8. .net 数据源DataSet 转换成模型

    /// <summary> /// DataSet转换成model 自动赋值返回集合 /// </summary> /// <typeparam name="T ...

  9. Java日期时间API系列4-----Jdk7及以前的日期时间类的线程安全问题

    1.Date类为可变的,在多线程并发环境中会有线程安全问题. (1)可以使用锁来处理并发问题. (2)使用JDK8  Instant 或 LocalDateTime替代. 2.Calendar的子类为 ...

  10. Python上下文管理器的使用

    上下文管理器可以控制代码块执行前的准备动作,以及执行后的清理动作. 创建一个上下文管理器类的步骤:(1)一个__init__方法,来完成初始化(可选)(2)一个__enter__方法,来完成所有建立工 ...