[BZOJ3451]Normal(点分治+FFT)

题面

给你一棵 n个点的树,对这棵树进行随机点分治,每次随机一个点作为分治中心。定义消耗时间为每层分治的子树大小之和,求消耗时间的期望。

分析

根据期望的线性性,答案是\(\sum_{i=1}^n(i的期望子树大小)=\sum_{i=1}^n \sum_{j=1}^n [j在i的点分治子树内]\)

考虑j在i的点分治子树内的条件,显然i到j的路径上的所有点中,i是第一个被选择为分治中心的。否则如果选的点不是i,那么i和j会被分到两棵子树中。第一个被选择的的概率是\(\frac{1}{dist(i,j)+1}\)(\(dist(i,j)\)表示i到j的距离)。那么上式就可以写成\(\sum_{i=1}^n \sum_{j=1}^n \frac{1}{dist(i,j)+1}\)

转换一下,设\(cnt[d]\)表示\(dist(i,j)=d\)的\((i,j)\)个数,那么答案为\(\sum_{d=0}^{n-1} \frac{cnt[d]}{d+1}\)。考虑如何求\(cnt[k]\)

我们在点分治的过程中,dfs出深度为i的节点个数cd[i]。那么求经过根节点的答案的时候就是\(cnt[i]=\sum_{j=0}^i cd[j]cd[i-j]\).容易看出这是一个卷积的形式,直接用cd和自身FFT求卷积即可。

注意最后要像一般的点分治一样容斥一下.

时间复杂度满足递推式\(T(n)=2T(\frac{n}{2})+\frac{1}{2}n\log n\).根据主定理的第二种情况,答案是\(\Theta (n\log^2 n)\)

代码

  1. #include<iostream>
  2. #include<cstdio>
  3. #include<cstring>
  4. #include<cmath>
  5. #define maxn 200000
  6. using namespace std;
  7. typedef long double db;
  8. typedef long long ll;
  9. const db pi=acos(-1.0);
  10. struct com{//复数类
  11. double real;
  12. double imag;
  13. com(){
  14. }
  15. com(double _real,double _imag){
  16. real=_real;
  17. imag=_imag;
  18. }
  19. com(double x){
  20. real=x;
  21. imag=0;
  22. }
  23. void operator = (const com x){
  24. this->real=x.real;
  25. this->imag=x.imag;
  26. }
  27. void operator = (const double x){
  28. this->real=x;
  29. this->imag=0;
  30. }
  31. friend com operator + (com p,com q){
  32. return com(p.real+q.real,p.imag+q.imag);
  33. }
  34. friend com operator + (com p,double q){
  35. return com(p.real+q,p.imag);
  36. }
  37. void operator += (com q){
  38. *this=*this+q;
  39. }
  40. void operator += (double q){
  41. *this=*this+q;
  42. }
  43. friend com operator - (com p,com q){
  44. return com(p.real-q.real,p.imag-q.imag);
  45. }
  46. friend com operator - (com p,double q){
  47. return com(p.real-q,p.imag);
  48. }
  49. void operator -= (com q){
  50. *this=*this-q;
  51. }
  52. void operator -= (double q){
  53. *this=*this-q;
  54. }
  55. friend com operator * (com p,com q){
  56. return com(p.real*q.real-p.imag*q.imag,p.real*q.imag+p.imag*q.real);
  57. }
  58. friend com operator * (com p,double q){
  59. return com(p.real*q,p.imag*q);
  60. }
  61. void operator *= (com q){
  62. *this=(*this)*q;
  63. }
  64. void operator *= (double q){
  65. *this=(*this)*q;
  66. }
  67. friend com operator / (com p,double q){
  68. return com(p.real/q,p.imag/q);
  69. }
  70. void operator /= (double q){
  71. *this=(*this)/q;
  72. }
  73. void print(){
  74. printf("%lf + %lf i ",real,imag);
  75. }
  76. };
  77. void fft(com *x,int n,int type){
  78. static int rev[maxn+5];
  79. int dn=1,k=0;
  80. while(dn<n){
  81. dn*=2;
  82. k++;
  83. }
  84. for(int i=0;i<n;i++) rev[i]=(rev[i>>1]>>1)|((i&1)<<(k-1));
  85. for(int i=0;i<n;i++) if(i<rev[i]) swap(x[i],x[rev[i]]);
  86. for(int len=1;len<n;len*=2){
  87. int sz=len*2;
  88. com wn1=com(cos(2*pi/sz),sin(2*pi/sz)*type);
  89. for(int l=0;l<n;l+=sz){
  90. int r=l+len-1;
  91. com wnk=1;
  92. for(int i=l;i<=r;i++){
  93. com tmp=x[i+len];
  94. x[i+len]=x[i]-wnk*tmp;
  95. x[i]=x[i]+wnk*tmp;
  96. wnk*=wn1;
  97. }
  98. }
  99. }
  100. if(type==-1) for(int i=0;i<n;i++) x[i]/=n;
  101. }
  102. void mul(com *a,com *b,com *ans,int n){//封装多项式乘法
  103. fft(a,n,1);
  104. if(a!=b) fft(b,n,1);
  105. for(int i=0;i<n;i++) ans[i]=a[i]*b[i];
  106. fft(ans,n,-1);
  107. }
  108. struct edge{
  109. int from;
  110. int to;
  111. int next;
  112. }E[maxn*2+5];
  113. int head[maxn+5];
  114. int esz=1;
  115. void add_edge(int u,int v){
  116. esz++;
  117. E[esz].from=u;
  118. E[esz].to=v;
  119. E[esz].next=head[u];
  120. head[u]=esz;
  121. }
  122. bool vis[maxn+5];
  123. int sz[maxn+5],f[maxn+5];
  124. int root;
  125. int tot_sz;
  126. void get_root(int x,int fa){
  127. sz[x]=1;
  128. f[x]=0;
  129. for(int i=head[x];i;i=E[i].next){
  130. int y=E[i].to;
  131. if(y!=fa&&!vis[y]){
  132. get_root(y,x);
  133. sz[x]+=sz[y];
  134. f[x]=max(f[x],sz[y]);
  135. }
  136. }
  137. f[x]=max(f[x],tot_sz-sz[x]);
  138. if(f[x]<f[root]) root=x;
  139. }
  140. int maxd;
  141. com ff[maxn+5];//当前子树中深度为x的节点个数
  142. com res[maxn+5];
  143. ll cnt[maxn+5];
  144. void get_deep(int x,int fa,int d){
  145. ff[d]+=1;
  146. maxd=max(maxd,d);
  147. for(int i=head[x];i;i=E[i].next){
  148. int y=E[i].to;
  149. if(y!=fa&&!vis[y]){
  150. get_deep(y,x,d+1);
  151. }
  152. }
  153. }
  154. void calc(int x,int d,int type){
  155. maxd=0;
  156. get_deep(x,0,d);
  157. int dn=1,k=0;
  158. while(dn<=maxd*2){
  159. dn*=2;
  160. k++;
  161. }
  162. mul(ff,ff,res,dn);//卷积
  163. for(int i=0;i<=maxd*2;i++) cnt[i]+=(ll)(res[i].real+0.5)*type;//用卷积结果更新cnt
  164. for(int i=0;i<=dn;i++) ff[i]=0;
  165. }
  166. void solve(int x){
  167. vis[x]=1;
  168. calc(x,0,1);
  169. for(int i=head[x];i;i=E[i].next){
  170. int y=E[i].to;
  171. if(!vis[y]){
  172. calc(y,1,-1);//容斥,减去一条边经过两次的答案
  173. root=0;
  174. tot_sz=sz[y];
  175. get_root(y,0);
  176. solve(root);
  177. }
  178. }
  179. }
  180. int n;
  181. int main(){
  182. int u,v;
  183. scanf("%d",&n);
  184. for(int i=1;i<n;i++){
  185. scanf("%d %d",&u,&v);
  186. u++;
  187. v++;
  188. add_edge(u,v);
  189. add_edge(v,u);
  190. }
  191. f[0]=n+1;
  192. root=0;
  193. tot_sz=n;
  194. get_root(1,0);
  195. solve(root);
  196. db ans=0;
  197. for(int i=0;i<=n-1;i++){
  198. ans+=(db)cnt[i]*1/(i+1);
  199. }
  200. printf("%.4Lf\n",ans);
  201. }

[BZOJ3451]Normal(点分治+FFT)的更多相关文章

  1. [BZOJ3451]normal 点分治,NTT

    [BZOJ3451]normal 点分治,NTT 好久没更博了,咕咕咕. BZOJ3451权限题,上darkbzoj交吧. 一句话题意,求随机点分治的期望复杂度. 考虑计算每个点对的贡献:如果一个点在 ...

  2. 【BZOJ3451】Tyvj1953 Normal 点分治+FFT+期望

    [BZOJ3451]Tyvj1953 Normal Description 某天WJMZBMR学习了一个神奇的算法:树的点分治!这个算法的核心是这样的:消耗时间=0Solve(树 a) 消耗时间 += ...

  3. 【BZOJ3451】Tyvj1953 Normal - 点分治+FFT

    题目来源:NOI2019模拟测试赛(七) 非原题面,题意有略微区别 题意: 吐槽: 心态崩了. 好不容易场上想出一题正解,写了三个小时结果写了个假的点分治,卡成$O(n^2)$ 我退役吧. 题解: 原 ...

  4. BZOJ 3451: Tyvj1953 Normal 点分治+FFT

    根据期望的线性性,我们算出每个点期望被计算次数,然后进行累加. 考虑点 $x$ 对点 $y$ 产生了贡献,那么说明 $(x,y)$ 之间的点中 $x$ 是第一个被删除的. 这个期望就是 $\frac{ ...

  5. [BZOJ3451][Tyvj1953]Normal(点分治+FFT)

    https://www.cnblogs.com/GXZlegend/p/8611948.html #include<cmath> #include<cstdio> #inclu ...

  6. 3451: Tyvj1953 Normal 点分治 FFT

    国际惯例的题面:代价理解为重心和每个点这个点对的代价.根据期望的线性性,我们枚举每个点,计算会产生的ij点对的代价即可.那么,i到j的链上,i必须是第一个被选择的点.对于i来说,就是1/dis(i,j ...

  7. BZOJ3451 Tyvj1953 Normal 点分治 多项式 FFT

    原文链接https://www.cnblogs.com/zhouzhendong/p/BZOJ3451.html 题目传送门 - BZOJ3451 题意 给定一棵有 $n$ 个节点的树,在树上随机点分 ...

  8. 【bzoj3451】Tyvj1953 Normal 期望+树的点分治+FFT

    题目描述 给你一棵 $n$ 个点的树,对这棵树进行随机点分治,每次随机一个点作为分治中心.定义消耗时间为每层分治的子树大小之和,求消耗时间的期望. 输入 第一行一个整数n,表示树的大小接下来n-1行每 ...

  9. BNUOJ 51279[组队活动 Large](cdq分治+FFT)

    传送门 大意:ACM校队一共有n名队员,从1到n标号,现在n名队员要组成若干支队伍,每支队伍至多有m名队员,求一共有多少种不同的组队方案.两个组队方案被视为不同的,当且仅当存在至少一名队员在两种方案中 ...

随机推荐

  1. 【NOIP2016提高A组模拟9.15】Osu

    题目 分析 考虑二分答案, 二分小数显然是不可取的,那么我们将所有可能的答案求出来,记录在一个数组上,排个序(C++调用函数很容易超时,手打快排,时间复杂度约为\(O(>8*10^7)\),但相 ...

  2. Send Email

    private string SendEmail(string mailTo, string body, ref int sendresult) { string errorEmailAddress ...

  3. UVA 11988 Broken Keyboard (a.k.a. Beiju Text)(链表)

    题目代号:UVA 11988 题目链接:https://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&pa ...

  4. 记一次CTF实验吧的代码审计

    0X01 0X01 Burp抓包找到hint 访问地址看源码 <?php $info = ""; $req = []; $flag="xxxxxxxxxx" ...

  5. [CSP-S模拟测试]:括号密码(贪心)

    题目描述 在“无限神机”的核心上,有一个奇怪的括号密码,密码初始已经有一个括号序列,有$n$个限制条件,每个限制条件描述为$l_i$和$r_i$,表示区间$[l_i,r_i]$的括号序列必须合法.调整 ...

  6. Java的LinkedList底层源码分析

    首先我们先说一下,源码里可以看出此类不仅仅用双向链表实现了队列数据结构的功能,还提供了链表数据结构的功能.

  7. legend3---PHP使用阿里云短信服务

    legend3---PHP使用阿里云短信服务 一.总结 一句话总结: 使用步骤照官方文档,代码拷贝即可 1.php使用阿里云短信服务的步骤? 入驻阿里云->开通短信服务->获取Access ...

  8. php 判断字符串包含

    PHP语言是一个功能强大的嵌入式HTML脚本语言,它的易用性让许多程序员选择使用.PHP判断字符串的包含,可以使用PHP的内置函数 strstr,strpos,stristr直接进行判断.也可以通过e ...

  9. AI工程师职业规划和学习路线完整版

    AI工程师职业规划和学习路线完整版   如何成为一名机器学习算法工程师 成为一名合格的开发工程师不是一件简单的事情,需要掌握从开发到调试到优化等一系列能 力,这些能力中的每一项掌握起来都需要足够的努力 ...

  10. 微信小程序 API 路由

    路由:由于页面的跳转: wx.switchTab() 跳转到 tabBar 页面,并关闭掉其他所有非 tabBar 页面: 参数:为对象, 对象的属性: url:需要跳转的 tabBar 的页面路径( ...