【BZOJ3451】Tyvj1953 Normal 点分治+FFT+期望
【BZOJ3451】Tyvj1953 Normal
Description
某天WJMZBMR学习了一个神奇的算法:树的点分治!
这个算法的核心是这样的:
消耗时间=0
Solve(树 a)
消耗时间 += a 的 大小
如果 a 中 只有 1 个点
退出
否则在a中选一个点x,在a中删除点x
那么a变成了几个小一点的树,对每个小树递归调用Solve
我们注意到的这个算法的时间复杂度跟选择的点x是密切相关的。
如果x是树的重心,那么时间复杂度就是O(nlogn)
但是由于WJMZBMR比较傻逼,他决定随机在a中选择一个点作为x!
Sevenkplus告诉他这样做的最坏复杂度是O(n^2)
但是WJMZBMR就是不信>_<。。。
于是Sevenkplus花了几分钟写了一个程序证明了这一点。。。你也试试看吧^_^
现在给你一颗树,你能告诉WJMZBMR他的傻逼算法需要的期望消耗时间吗?(消耗时间按在Solve里面的那个为标准)
Input
第一行一个整数n,表示树的大小
接下来n-1行每行两个数a,b,表示a和b之间有一条边
注意点是从0开始标号的
Output
一行一个浮点数表示答案
四舍五入到小数点后4位
如果害怕精度跪建议用long double或者extended
Sample Input
0 1
1 2
Sample Output
HINT
n<=30000
题解:由于期望永远是可加的,所以我们可以讨论每个点对答案的贡献(即每个点在点分树上的深度)。对于x,y,我们统计y对x的贡献,即y成为x在点分树上的祖先的概率。y是x在点分树上的祖先当且仅当y是x-y路径上的第一个被选中的点。由于路径上每个点第一次被选中的概率都是相同的,所以概率就是1/dis(x,y)。具体地,我们的答案=$\sum\limits_{x=1}^n\sum\limits_{y=1}^n {1\over dis(x,y)}$。
所以我们希望对于任意的dis,统计出有多少点对之间的距离=dis,这个点分治+FFT即可。不过这里的点分治最好采用容斥的写法,即当以x为分治中心时,先统计出x子树中任意两点间的答案,再将两点再同一个儿子中的情况减去。
#include <cstdio>
#include <cstring>
#include <iostream>
#include <cmath>
#define pi acos(-1.0)
using namespace std;
const int maxn=30010;
struct cp
{
double x,y;
cp () {}
cp (double a,double b){x=a,y=b;}
cp operator + (const cp &a) const {return cp(x+a.x,y+a.y);}
cp operator - (const cp &a) const {return cp(x-a.x,y-a.y);}
cp operator * (const cp &a) const {return cp(x*a.x-y*a.y,x*a.y+y*a.x);}
}A[maxn<<2];
long double Ans;
int n,rt,cnt,mx,tot,md;
int ans[maxn<<2];
int to[maxn<<1],next[maxn<<1],head[maxn],vis[maxn],siz[maxn],dep[maxn];
inline int rd()
{
int ret=0,f=1; char gc=getchar();
while(gc<'0'||gc>'9') {if(gc=='-')f=-f; gc=getchar();}
while(gc>='0'&&gc<='9') ret=ret*10+gc-'0',gc=getchar();
return ret*f;
}
void add(int a,int b)
{
to[cnt]=b,next[cnt]=head[a],head[a]=cnt++;
}
void FFT(cp *a,int len,int f)
{
int i,j,k,h;
cp t;
for(i=k=0;i<len;i++)
{
if(i>k) swap(a[i],a[k]);
for(j=len>>1;(k^=j)<j;j>>=1);
}
for(h=2;h<=len;h<<=1)
{
cp wn(cos(2*pi*f/h),sin(2*pi*f/h));
for(j=0;j<len;j+=h)
{
cp w(1,0);
for(k=j;k<j+h/2;k++) t=a[k+h/2]*w,a[k+h/2]=a[k]-t,a[k]=a[k]+t,w=w*wn;
}
}
}
void getr(int x,int fa)
{
siz[x]=1;
int tmp=0;
for(int i=head[x];i!=-1;i=next[i])
if(to[i]!=fa&&!vis[to[i]]) getr(to[i],x),siz[x]+=siz[to[i]],tmp=max(tmp,siz[to[i]]);
tmp=max(tmp,tot-siz[x]);
if(tmp<mx) mx=tmp,rt=x;
}
void getd(int x,int fa,int dep)
{
A[dep].x+=1,md=max(md,dep);
for(int i=head[x];i!=-1;i=next[i]) if(to[i]!=fa&&!vis[to[i]]) getd(to[i],x,dep+1);
}
void calc(int x,int f)
{
md=0,getd(x,0,0);
int i,len;
for(len=1;len<=md*2;len<<=1);
FFT(A,len,1);
for(i=0;i<len;i++) A[i]=A[i]*A[i];
FFT(A,len,-1);
if(f==1) for(i=0;i<len;i++) ans[i+1]+=int(A[i].x/len+0.1);
else for(i=0;i<len;i++) ans[i+3]-=int(A[i].x/len+0.1);
memset(A,0,sizeof(A[0])*len);
}
void dfs(int x)
{
vis[x]=1;
calc(x,1);
for(int i=head[x];i!=-1;i=next[i]) if(!vis[to[i]]) calc(to[i],0),tot=siz[to[i]],mx=1<<30,getr(to[i],x),dfs(rt);
}
int main()
{
n=rd();
int i,a,b;
memset(head,-1,sizeof(head));
for(i=1;i<n;i++) a=rd()+1,b=rd()+1,add(a,b),add(b,a);
tot=n,mx=1<<30,getr(1,0),dfs(rt);
for(i=1;i<=n;i++)
{
Ans+=(long double)ans[i]/i;
}
printf("%.4lf",(double)Ans);
return 0;
}
【BZOJ3451】Tyvj1953 Normal 点分治+FFT+期望的更多相关文章
- [BZOJ3451][Tyvj1953]Normal(点分治+FFT)
https://www.cnblogs.com/GXZlegend/p/8611948.html #include<cmath> #include<cstdio> #inclu ...
- BZOJ3451 Tyvj1953 Normal 点分治 多项式 FFT
原文链接https://www.cnblogs.com/zhouzhendong/p/BZOJ3451.html 题目传送门 - BZOJ3451 题意 给定一棵有 $n$ 个节点的树,在树上随机点分 ...
- 【BZOJ3451】Tyvj1953 Normal - 点分治+FFT
题目来源:NOI2019模拟测试赛(七) 非原题面,题意有略微区别 题意: 吐槽: 心态崩了. 好不容易场上想出一题正解,写了三个小时结果写了个假的点分治,卡成$O(n^2)$ 我退役吧. 题解: 原 ...
- BZOJ 3451: Tyvj1953 Normal 点分治+FFT
根据期望的线性性,我们算出每个点期望被计算次数,然后进行累加. 考虑点 $x$ 对点 $y$ 产生了贡献,那么说明 $(x,y)$ 之间的点中 $x$ 是第一个被删除的. 这个期望就是 $\frac{ ...
- 3451: Tyvj1953 Normal 点分治 FFT
国际惯例的题面:代价理解为重心和每个点这个点对的代价.根据期望的线性性,我们枚举每个点,计算会产生的ij点对的代价即可.那么,i到j的链上,i必须是第一个被选择的点.对于i来说,就是1/dis(i,j ...
- [BZOJ3451]Normal(点分治+FFT)
[BZOJ3451]Normal(点分治+FFT) 题面 给你一棵 n个点的树,对这棵树进行随机点分治,每次随机一个点作为分治中心.定义消耗时间为每层分治的子树大小之和,求消耗时间的期望. 分析 根据 ...
- BZOJ3451: Tyvj1953 Normal
题解: 好神的一道题.蒟蒻只能膜拜题解. 考虑a对b的贡献,如果a是a-b路径上第一个删除的点,那么给b贡献1. 所以转化之后就是求sigma(1/dist(i,j)),orz!!! 如果不是分母的话 ...
- BZOJ3451 Tyvj1953 Normal 【期望 + 点分治 + NTT】
题目链接 BZOJ3451 题解 考虑每个点产生的贡献,即为该点在点分树中的深度期望值 由于期望的线性,最后的答案就是每个点贡献之和 对于点对\((i,j)\),考虑\(j\)成为\(i\)祖先的概率 ...
- 【bzoj3451】Tyvj1953 Normal 期望+树的点分治+FFT
题目描述 给你一棵 $n$ 个点的树,对这棵树进行随机点分治,每次随机一个点作为分治中心.定义消耗时间为每层分治的子树大小之和,求消耗时间的期望. 输入 第一行一个整数n,表示树的大小接下来n-1行每 ...
随机推荐
- 将Map<String, List<Map<String,Object>>>进行排序
首先我贴上我的代码,刚开始我也不知道怎么排序还写了一些方法,最后请教群里的大神解决了 public Map<String, List<Map<String,Object>> ...
- Linux centos 时间不同步 date 和 hwclock 时间不一致
Linux centos 时间不同步 date 和 hwclock 网站有两台服务器,date 查看差3分钟 在应用 APP倒计时 与 直播,时时性较强的功能应用中 请求服务器后返回的服务器时间与数据 ...
- shell date获取时间值
使用Shell获取上一个月.星期的时间范围 #!/bin/bash date_today=`date -d '1 day ago' +%Y%m%d` #最近7天 date_befor_7day=` ...
- Eclipse的调试功能(转)(让Eclipse也能有VS的即时窗口那样的即时代码调试功能)
前言:可以很明确的说明,eclipse也有像vs那样的即时窗口来运行即时代码的功能. 调试的界面如下: 如果要像vs那样的即时调试功能,需要做一些设置,就是Expressions功能. 开通步骤:Wi ...
- Windows 远程桌面文件传输的方法
实现电脑的远程连接以后,很多时候会需要进行主机间的文件传输,这个时候就可以用系统自带的远程连接里的磁盘映射来完成,详细如下: 远程桌面程序内置了映射磁盘的功能,通过这个功能可以实现远程登录服务器时自动 ...
- iOS博客列表
国外 iOSDevWeekly NSHipster NSBlog objcio Raywenderlich Bignerdranch NSScreencast 需FQ Pilky.me jeremyw ...
- 算法之美--2.2 Array
2016-12-02 00:24:12 利用抽象数据类型实现Array:主要用C++模板实现数组类:体会一个完整的C++类的写法,能顺利写出来的人不多了,特别是对复制构造函数和重载= []运算, ...
- Linux下Reids的安装和使用
简单记录一下 redis的官网:https://redis.io/ 官网介绍: Installation Download, extract and compile Redis with: $ wge ...
- 【音乐App】—— Vue-music 项目学习笔记:推荐页面开发
前言:以下内容均为学习慕课网高级实战课程的实践爬坑笔记. 上一篇总结了项目概述.项目准备.页面骨架搭建.这一篇重点梳理推荐页面开发.项目github地址:https://github.com/66We ...
- JAVA Eclipse开发Android程序会经常闪退是怎么回事
类没有new就直接用会闪退,比如下面我在前面声明了ClientSocket的类,点击连接按钮之后,如果不先new就直接Connect就会闪退 你在当前页面创建findViewById的时候,如果找 ...