[BZOJ3451]Normal(点分治+FFT)
[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)的更多相关文章
- [BZOJ3451]normal 点分治,NTT
[BZOJ3451]normal 点分治,NTT 好久没更博了,咕咕咕. BZOJ3451权限题,上darkbzoj交吧. 一句话题意,求随机点分治的期望复杂度. 考虑计算每个点对的贡献:如果一个点在 ...
- 【BZOJ3451】Tyvj1953 Normal 点分治+FFT+期望
[BZOJ3451]Tyvj1953 Normal Description 某天WJMZBMR学习了一个神奇的算法:树的点分治!这个算法的核心是这样的:消耗时间=0Solve(树 a) 消耗时间 += ...
- 【BZOJ3451】Tyvj1953 Normal - 点分治+FFT
题目来源:NOI2019模拟测试赛(七) 非原题面,题意有略微区别 题意: 吐槽: 心态崩了. 好不容易场上想出一题正解,写了三个小时结果写了个假的点分治,卡成$O(n^2)$ 我退役吧. 题解: 原 ...
- BZOJ 3451: Tyvj1953 Normal 点分治+FFT
根据期望的线性性,我们算出每个点期望被计算次数,然后进行累加. 考虑点 $x$ 对点 $y$ 产生了贡献,那么说明 $(x,y)$ 之间的点中 $x$ 是第一个被删除的. 这个期望就是 $\frac{ ...
- [BZOJ3451][Tyvj1953]Normal(点分治+FFT)
https://www.cnblogs.com/GXZlegend/p/8611948.html #include<cmath> #include<cstdio> #inclu ...
- 3451: Tyvj1953 Normal 点分治 FFT
国际惯例的题面:代价理解为重心和每个点这个点对的代价.根据期望的线性性,我们枚举每个点,计算会产生的ij点对的代价即可.那么,i到j的链上,i必须是第一个被选择的点.对于i来说,就是1/dis(i,j ...
- BZOJ3451 Tyvj1953 Normal 点分治 多项式 FFT
原文链接https://www.cnblogs.com/zhouzhendong/p/BZOJ3451.html 题目传送门 - BZOJ3451 题意 给定一棵有 $n$ 个节点的树,在树上随机点分 ...
- 【bzoj3451】Tyvj1953 Normal 期望+树的点分治+FFT
题目描述 给你一棵 $n$ 个点的树,对这棵树进行随机点分治,每次随机一个点作为分治中心.定义消耗时间为每层分治的子树大小之和,求消耗时间的期望. 输入 第一行一个整数n,表示树的大小接下来n-1行每 ...
- BNUOJ 51279[组队活动 Large](cdq分治+FFT)
传送门 大意:ACM校队一共有n名队员,从1到n标号,现在n名队员要组成若干支队伍,每支队伍至多有m名队员,求一共有多少种不同的组队方案.两个组队方案被视为不同的,当且仅当存在至少一名队员在两种方案中 ...
随机推荐
- C++中的字符数组、字符指、字符串针(腾讯)
一.字符数组 1.定义时进行初始化的方式 (1)char c[12]={'I',' ','a','m',' ','h','a','p','p','y'};//最后两个元素自动补‘\0’(不是空格),其 ...
- 【NOIP2016提高A组模拟9.24】天使的分裂
题目 分析 这题可以递推, 但是\(O(n)\)还是会超时, 就用矩阵快速幂. #include <cmath> #include <iostream> #include &l ...
- 【leetcode】689. Maximum Sum of 3 Non-Overlapping Subarrays
题目如下: In a given array nums of positive integers, find three non-overlapping subarrays with maximum ...
- 对Node.js 中的依赖管理的研究-----------------引用
nodejs依赖: dependencies devDependencies peerDependencies bundledDependencies optionalDependenc ...
- python 面向对象_1
self #self 是相当于c++的 this指针 class Ball: def setName(self,name): self.name = name def kick(self): prin ...
- linux登陆客户端自动执行命令
登陆客户端的时候,检查一下磁盘空间,内存,或是谁在线,每次都要手动去敲命令. 小技巧: cd ~ vi .bashrc 添加: echo "####Check Disk Use####&qu ...
- String,Integer,int类型之间的相互转换
String, Integer, int 三种类型之间可以两两进行转换 1. 基本数据类型到包装数据类型的转换 int -> Integer (两种方法) Integer it1 = new I ...
- 实战build-react(二)-------引入Ant Design
安装 Ant Design npm install antd --save 或 yarn add antd 注释:https://www.jianshu.com/p/21caf40ee93e(cop ...
- VSCode支持jsx自动补全
点击settings.json中编辑, 把这段话加上去就可以了 "emmet.includeLanguages": { "javascript": " ...
- HY中考游记
回首三年荏苒,还是有许多忘不了,有始有终,最后以一篇游记来记录落幕吧 Day -inf 为了准备中考从机(颓)房回到学校了,停课这么久,也该好好备考了 希望能回到以前的文化课水平QAQ Day -? ...