[关闭]
@Junlier 2018-08-21T09:16:56.000000Z 字数 2501 阅读 1832

点分治

图论——点分治
嗯,蒟蒻我刚学的就记录一下
以洛谷的tree为模板讲解:洛谷题目传送门

树的重心

了解点分治之前,首先要知道什么是重心(要用到)
简单来说,就是子树最小的那个节点,我们需要O(n)地找到他来保证复杂度

  1. void get_root(rg int now,rg int fm)
  2. {
  3. size[now]=1;rg int num=0;//size[]记录子树大小,num[]为当前的最大子树
  4. for(rg int i=hd[now];i;i=ljl[i].nxt)//遍历
  5. {
  6. rg int qw=ljl[i].to;
  7. if(qw==fm||vis[qw])continue;//先不管这个vis[]
  8. get_root(qw,now);
  9. size[now]+=size[qw];
  10. num=max(num,size[qw]);
  11. }
  12. num=max(num,tot-size[now]);//tot是总点数
  13. //tot-size[now]是父亲那边(不是儿子)的“子树”大小
  14. if(Max>num)Max=num,root=now;//是否更新
  15. }

个人认为这里还是比较简单的,自己画个图简单明了了

点分树

找到重心后把重心删掉,变成两个连通块,再分别找重心,把重心与之前找的重心相连边,不断递归建成一棵新树,就是点分树
&&优点:严格log层(不信自己验证)
这道题暂时不要用,但是是点分治的一个重要知识点……

点分治

首先了解几个特点(暂时不用理解,等下自然就懂)

  • 边建边算
  • 常见问题:
    1.是否存在……的路径
    2.满足……的路径有几条
  • 只要点分治之后的处理是线性的,那么总的复杂度一定是nlogn(只要你不瞎搞)

以tree这道题为例,找到一个重心就处理一定过重心的那条路径的答案,别问我为什么,反正这样是对的,而且时间优秀,不会算重//=_=
嗯,这里有点长了,不想写了,所以安利一波机房大佬的博客理解吧,一时半会真的讲不清,至少我问了很久才懂(主要是不想画图来讲解了,画图就很容易理解了)

YCB blog

code还是不少

  1. #include<iostream>
  2. #include<cstdlib>
  3. #include<cstdio>
  4. #include<cmath>
  5. #include<cstring>
  6. #include<iomanip>
  7. #include<algorithm>
  8. #include<ctime>
  9. #include<queue>
  10. #include<stack>
  11. #include<vector>
  12. #define rg register
  13. #define il inline
  14. #define lst long long
  15. #define ldb long double
  16. #define N 40050
  17. using namespace std;
  18. const int Inf=1e9;
  19. int n,K,cnt,tot,ans;
  20. int Max,root,le,ri;
  21. struct EDGE{
  22. int to,nxt,v;
  23. }ljl[N<<1];
  24. int hd[N];
  25. int size[N],vis[N];
  26. int Q[N],dis[N];
  27. il int read()
  28. {
  29. rg int s=0,m=0;rg char ch=getchar();
  30. while(ch<'0'||ch>'9'){if(ch=='-')m=1;ch=getchar();}
  31. while(ch>='0'&&ch<='9')s=(s<<3)+(s<<1)+(ch^48),ch=getchar();
  32. return m?-s:s;
  33. }
  34. il void add(rg int p,rg int q,rg int o)
  35. {
  36. ljl[++cnt]=(EDGE){q,hd[p],o};hd[p]=cnt;
  37. }
  38. void get_root(rg int now,rg int fm)
  39. {
  40. size[now]=1;rg int num=0;
  41. for(rg int i=hd[now];i;i=ljl[i].nxt)
  42. {
  43. rg int qw=ljl[i].to;
  44. if(qw==fm||vis[qw])continue;
  45. get_root(qw,now);
  46. size[now]+=size[qw];
  47. num=max(num,size[qw]);
  48. }
  49. num=max(num,tot-size[now]);
  50. if(Max>num)Max=num,root=now;
  51. }
  52. void get_dis(rg int now,rg int fm)
  53. {
  54. Q[++ri]=dis[now];
  55. for(rg int i=hd[now];i;i=ljl[i].nxt)
  56. {
  57. rg int qw=ljl[i].to;
  58. if(vis[qw]||qw==fm)continue;
  59. dis[qw]=dis[now]+ljl[i].v;
  60. get_dis(qw,now);
  61. }
  62. }
  63. il int Query(rg int now,rg int base)
  64. {
  65. rg int res=0;
  66. ri=0;dis[now]=base;
  67. le=1;get_dis(now,0);
  68. sort(Q+1,Q+ri+1);
  69. while(le<=ri)
  70. {
  71. if(Q[le]+Q[ri]<=K)res+=ri-le,le++;
  72. else ri--;
  73. }
  74. return res;
  75. }
  76. void divide(rg int now,rg int fm)
  77. {
  78. ans+=Query(now,0);vis[now]=1;
  79. rg int all=tot;
  80. for(rg int i=hd[now];i;i=ljl[i].nxt)
  81. {
  82. rg int qw=ljl[i].to;
  83. if(qw==fm||vis[qw])continue;
  84. ans-=Query(qw,ljl[i].v);
  85. tot=size[now]>size[qw]?size[qw]:all-size[qw];Max=Inf;
  86. get_root(qw,0),divide(qw,0);
  87. }
  88. }
  89. int main()
  90. {
  91. n=read();
  92. for(rg int i=1;i<n;++i)
  93. {
  94. rg int p=read(),q=read(),o=read();
  95. add(p,q,o),add(q,p,o);
  96. }
  97. K=read();
  98. tot=n,Max=Inf;
  99. get_root(1,0);
  100. divide(root,0);
  101. printf("%d\n",ans);
  102. return 0;
  103. }
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注