[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)\)

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#define maxn 200000
using namespace std;
typedef long double db;
typedef long long ll;
const db pi=acos(-1.0);
struct com{//复数类
double real;
double imag;
com(){ }
com(double _real,double _imag){
real=_real;
imag=_imag;
}
com(double x){
real=x;
imag=0;
}
void operator = (const com x){
this->real=x.real;
this->imag=x.imag;
}
void operator = (const double x){
this->real=x;
this->imag=0;
}
friend com operator + (com p,com q){
return com(p.real+q.real,p.imag+q.imag);
}
friend com operator + (com p,double q){
return com(p.real+q,p.imag);
}
void operator += (com q){
*this=*this+q;
}
void operator += (double q){
*this=*this+q;
}
friend com operator - (com p,com q){
return com(p.real-q.real,p.imag-q.imag);
}
friend com operator - (com p,double q){
return com(p.real-q,p.imag);
}
void operator -= (com q){
*this=*this-q;
}
void operator -= (double q){
*this=*this-q;
}
friend com operator * (com p,com q){
return com(p.real*q.real-p.imag*q.imag,p.real*q.imag+p.imag*q.real);
}
friend com operator * (com p,double q){
return com(p.real*q,p.imag*q);
}
void operator *= (com q){
*this=(*this)*q;
}
void operator *= (double q){
*this=(*this)*q;
}
friend com operator / (com p,double q){
return com(p.real/q,p.imag/q);
}
void operator /= (double q){
*this=(*this)/q;
}
void print(){
printf("%lf + %lf i ",real,imag);
}
};
void fft(com *x,int n,int type){
static int rev[maxn+5];
int dn=1,k=0;
while(dn<n){
dn*=2;
k++;
}
for(int i=0;i<n;i++) rev[i]=(rev[i>>1]>>1)|((i&1)<<(k-1));
for(int i=0;i<n;i++) if(i<rev[i]) swap(x[i],x[rev[i]]);
for(int len=1;len<n;len*=2){
int sz=len*2;
com wn1=com(cos(2*pi/sz),sin(2*pi/sz)*type);
for(int l=0;l<n;l+=sz){
int r=l+len-1;
com wnk=1;
for(int i=l;i<=r;i++){
com tmp=x[i+len];
x[i+len]=x[i]-wnk*tmp;
x[i]=x[i]+wnk*tmp;
wnk*=wn1;
}
}
}
if(type==-1) for(int i=0;i<n;i++) x[i]/=n;
}
void mul(com *a,com *b,com *ans,int n){//封装多项式乘法
fft(a,n,1);
if(a!=b) fft(b,n,1);
for(int i=0;i<n;i++) ans[i]=a[i]*b[i];
fft(ans,n,-1);
} struct edge{
int from;
int to;
int next;
}E[maxn*2+5];
int head[maxn+5];
int esz=1;
void add_edge(int u,int v){
esz++;
E[esz].from=u;
E[esz].to=v;
E[esz].next=head[u];
head[u]=esz;
} bool vis[maxn+5];
int sz[maxn+5],f[maxn+5];
int root;
int tot_sz;
void get_root(int x,int fa){
sz[x]=1;
f[x]=0;
for(int i=head[x];i;i=E[i].next){
int y=E[i].to;
if(y!=fa&&!vis[y]){
get_root(y,x);
sz[x]+=sz[y];
f[x]=max(f[x],sz[y]);
}
}
f[x]=max(f[x],tot_sz-sz[x]);
if(f[x]<f[root]) root=x;
} int maxd;
com ff[maxn+5];//当前子树中深度为x的节点个数
com res[maxn+5];
ll cnt[maxn+5]; void get_deep(int x,int fa,int d){
ff[d]+=1;
maxd=max(maxd,d);
for(int i=head[x];i;i=E[i].next){
int y=E[i].to;
if(y!=fa&&!vis[y]){
get_deep(y,x,d+1);
}
}
} void calc(int x,int d,int type){
maxd=0;
get_deep(x,0,d);
int dn=1,k=0;
while(dn<=maxd*2){
dn*=2;
k++;
}
mul(ff,ff,res,dn);//卷积
for(int i=0;i<=maxd*2;i++) cnt[i]+=(ll)(res[i].real+0.5)*type;//用卷积结果更新cnt
for(int i=0;i<=dn;i++) ff[i]=0;
} void solve(int x){
vis[x]=1;
calc(x,0,1);
for(int i=head[x];i;i=E[i].next){
int y=E[i].to;
if(!vis[y]){
calc(y,1,-1);//容斥,减去一条边经过两次的答案
root=0;
tot_sz=sz[y];
get_root(y,0);
solve(root);
}
}
} int n;
int main(){
int u,v;
scanf("%d",&n);
for(int i=1;i<n;i++){
scanf("%d %d",&u,&v);
u++;
v++;
add_edge(u,v);
add_edge(v,u);
}
f[0]=n+1;
root=0;
tot_sz=n;
get_root(1,0);
solve(root);
db ans=0;
for(int i=0;i<=n-1;i++){
ans+=(db)cnt[i]*1/(i+1);
}
printf("%.4Lf\n",ans);
}

[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. 【NOIP2014模拟8.24】小X 的道路修建

    题目 因为一场不小的地震,Y 省n 个城市之间的道路都损坏掉了,省长希望小X 将城市之间的道路重修一遍. 很多城市之间的地基都被地震破坏导致不能修路了,因此可供修建的道路只有m 条.因为施工队伍有限, ...

  2. 【清华集训2014】mex

    题目 有一个长度为n的数组{a1,a2,...,an}.m次询问,每次询问一个区间内最小没有出现过的自然数. 分析 显然,当\(a_i>n\)时,对答案没有影响,所以全部视为n+1. 有两种方法 ...

  3. Python 运算符Ⅴ

    Python位运算符 按位运算符是把数字看作二进制来进行计算的.Python中的按位运算法则如下: 下表中变量 a 为 60,b 为 13,二进制格式如下: 运算符 描述 实例 & 按位与运算 ...

  4. 可持久化Trie模板

    如果你了解过 01 Trie 和 可持久化线段树(例如 : 主席树 ).那么就比较好去可持久化 Trie 可持久化 Trie 当 01 Trie 用的时候能很方便解决一些原本 01 Trie 不能解决 ...

  5. Anaconda是如何进行版本管理的?

    创建不同的environments,在电脑中会有不同的文件夹 然后,当使用conda下载时,会下载到不同的env文件夹下(提前进行env切换) 那么不是在anaconda prompt命令行下下载的呢 ...

  6. 微信浏览器video播放视频踩坑

    video属性介绍 iOS的属性 playsinline On iPhone, video playsinline elements will now be allowed to play inlin ...

  7. asp.net ajax传递Json给aspx.cs后台 webmethod方法传递json

    1.提取一个向后台写入数据的方法 ///向后台cs页面请求数据的方法 function myPost(url,data,func) { $.ajax({ type: "post", ...

  8. JMS学习五(ActiveMQ的本地事务)

    1.ActiveMQ的本地事务 在一个JMS客户端,可以使用本地事务来组合消息的发送和接收.JMS Session接口提供了commit和rollback方法.事务提交意味着生产的所有消息被发送,消费 ...

  9. spring cloud:feign-hystrix

    producer 1. File-->new spring starter project 2.add dependency       <dependency> <group ...

  10. 假设高度已知,请写出三栏布局,其中左栏、右栏各为300px,中间自适应的五种方法

    假设高度已知,请写出三栏布局,其中左栏.右栏各为300px,中间自适应的五种方法 HTML CSS 页面布局 题目:假设高度已知,请写出三栏布局,其中左栏.右栏各为300px,中间自适应 <!D ...