题目来源:NOI2019模拟测试赛(七)

非原题面,题意有略微区别

题意:

吐槽:

心态崩了。

好不容易场上想出一题正解,写了三个小时结果写了个假的点分治,卡成$O(n^2)$

我退役吧。

题解:

原题是求随机树分治的期望深度和,题意相同。

对于一个点$x$,考虑点$y$是否能作为它在点分树上的祖先节点,显然当且仅当$y$在$x$到$y$的路径中第一个被选为分治中心时会对$x$产生1的贡献;

由于路径上所有点被选到的概率都是相等的,所以此时的期望就是$\frac{1}{dis(x,y)}$;

那么总的期望就是$\sum\limits_{x=1}^{n}\sum\limits_{y=1}^{n}\frac{1}{dis(x,y)}$;

在这里写个暴力即可爆踩我的假点分治;

考虑统计每种长度的路径条数,可以用点分治做,并且在点分树里合并时子树的期望是一个卷积的形式,因此可以用FFT来加速;

于是我就快乐的写了个点分治+FFT,获得了60分的好成绩;

为什么?参考这篇博客的证明,我最初的写法就是其中的第一种写法,搜完一个子树就和已经搜过的合并,这样做的话FFT的长度会是$子树中最大深度\times 根节点儿子个数=O(n^2)$的,正确的写法应该搜完再一起合并,或者像里面说的第二种方法一样直接搜当前子树,更新答案然后搜重心的每个儿子的子树,减去不合法的路径,这样子FFT的长度才是$O(n)$的。

代码:

假点分治(60pts):

 #include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<queue>
#define inf 2147483647
#define eps 1e-9
#define mod 1000000007
using namespace std;
typedef long long ll;
typedef double db;
const db pi=acos(-1.0); struct edge{
int v,next;
}a[];
int n,u,v,S,rt,mxd,bit,bitnum,tot=,cnt=,ans=,jc[],inv[],anss[],tp[],num[],s[],rev[],head[],mx[],siz[],dep[];
bool used[];
struct cp{
db a,b;
cp(){}
cp(db _a,db _b){
a=_a,b=_b;
}
friend cp operator +(cp a,cp b){return cp(a.a+b.a,a.b+b.b);}
friend cp operator -(cp a,cp b){return cp(a.a-b.a,a.b-b.b);}
friend cp operator *(cp a,cp b){return cp(a.a*b.a-a.b*b.b,a.a*b.b+a.b*b.a);}
friend cp operator *(cp a,db b){return cp(a.a*b,a.b*b);}
friend cp operator /(cp a,db b){return cp(a.a/b,a.b/b);}
}A[],B[],W[][];
void _(){
for(int i=;i<=(<<);i<<=){
W[i][]=cp(cos(pi/i),sin(pi/i));
W[i][]=cp(cos(pi/i),-sin(pi/i));
}
}
void fft(cp *s,int op){
for(int i=;i<bit;i++)if(i<rev[i])swap(s[i],s[rev[i]]);
for(int i=;i<bit;i<<=){
//cp w(cos(pi/i),op*sin(pi/i));
cp w=W[i][op==-];
for(int p=i<<,j=;j<bit;j+=p){
cp wk(,);
for(int k=j;k<i+j;k++,wk=wk*w){
cp x=s[k],y=wk*s[k+i];
s[k]=x+y;
s[k+i]=x-y;
}
}
}
if(op==-){
for(int i=;i<bit;i++){
s[i]=s[i]/(db)bit;
}
}
}
void add(int u,int v){
a[++tot].v=v;
a[tot].next=head[u];
head[u]=tot;
}
void mul(int *ret,int *a,int *b,int n){
for(bit=,bitnum=;bit<=n*;bit<<=)bitnum++;
for(int i=;i<=bit;i++){
rev[i]=(rev[i>>]>>)|((i&)<<(bitnum-));
}
for(int i=;i<bit;i++){
A[i]=cp((db)a[i],);
B[i]=cp(,);
}
for(int i=;i<=cnt;i++){
a[b[i]]++;
B[b[i]].a+=;
}
fft(A,);
fft(B,);
for(int i=;i<bit;i++)A[i]=A[i]*B[i];
fft(A,-);
for(int i=;i<bit;i++)ret[i]=(int)(A[i].a+0.5);
}
void getrt(int u,int fa){
mx[u]=;
siz[u]=;
for(int tmp=head[u];tmp!=-;tmp=a[tmp].next){
int v=a[tmp].v;
if(!used[v]&&v!=fa){
getrt(v,u);
siz[u]+=siz[v];
mx[u]=max(mx[u],siz[v]);
}
}
mx[u]=max(mx[u],S-mx[u]);
if(mx[u]<mx[rt])rt=u;
}
void getdep(int u,int fa,int dpt){
mxd=max(mxd,dpt);
s[++cnt]=dpt;
for(int tmp=head[u];tmp!=-;tmp=a[tmp].next){
int v=a[tmp].v;
if(!used[v]&&v!=fa){
getdep(v,u,dpt+);
}
}
}
void divide(int u){
used[u]=true;
mxd=;
for(int tmp=head[u];tmp!=-;tmp=a[tmp].next){
int v=a[tmp].v;
if(!used[v]){
cnt=;
getdep(v,u,);
mul(tp,num,s,mxd);
for(int i=;i<bit;i++)anss[i]+=tp[i];
}
}
for(int i=;i<=mxd;i++){
anss[i]+=num[i];
num[i]=;
}
for(int tmp=head[u];tmp!=-;tmp=a[tmp].next){
int v=a[tmp].v;
if(!used[v]){
S=siz[v];
rt=;
getrt(v,);
divide(rt);
}
}
}
int main(){
memset(head,-,sizeof(head));
_();
scanf("%d",&n);
jc[]=inv[]=inv[]=;
for(int i=;i<=n+;i++)inv[i]=(ll)(mod-mod/i)*inv[mod%i]%mod;
for(int i=;i<=n+;i++)jc[i]=(ll)jc[i-]*i%mod;
for(int i=;i<n;i++){
scanf("%d%d",&u,&v);
add(u,v);
add(v,u);
}
S=n;
mx[rt=]=;
getrt(,-);
divide(rt);
ans=n;
for(int i=;i<=n;i++){
ans=(ans+(ll)anss[i]*inv[i+]*%mod)%mod;
}
printf("%lld",(ll)ans*jc[n]%mod);
return ;
}

AC代码(100pts):

 #include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<queue>
#define inf 2147483647
#define eps 1e-9
#define mod 1000000007
using namespace std;
typedef long long ll;
typedef double db;
const db pi=acos(-1.0); struct edge{
int v,next;
}a[];
int n,u,v,S,rt,mxd,bit,bitnum,tot=,cnt=,ans=,jc[],inv[],anss[],tp[],num[],rev[],head[],mx[],siz[],dep[],dps[];
bool used[];
struct cp{
db a,b;
cp(){}
cp(db _a,db _b){
a=_a,b=_b;
}
friend cp operator +(cp a,cp b){return cp(a.a+b.a,a.b+b.b);}
friend cp operator -(cp a,cp b){return cp(a.a-b.a,a.b-b.b);}
friend cp operator *(cp a,cp b){return cp(a.a*b.a-a.b*b.b,a.a*b.b+a.b*b.a);}
friend cp operator *(cp a,db b){return cp(a.a*b,a.b*b);}
friend cp operator /(cp a,db b){return cp(a.a/b,a.b/b);}
}A[],B[],W[][];
void _(){
for(int i=;i<=(<<);i<<=){
W[i][]=cp(cos(pi/i),sin(pi/i));
W[i][]=cp(cos(pi/i),-sin(pi/i));
}
}
void fft(cp *s,int op){
for(int i=;i<bit;i++)if(i<rev[i])swap(s[i],s[rev[i]]);
for(int i=;i<bit;i<<=){
//cp w(cos(pi/i),op*sin(pi/i));
cp w=W[i][op==-];
for(int p=i<<,j=;j<bit;j+=p){
cp wk(,);
for(int k=j;k<i+j;k++,wk=wk*w){
cp x=s[k],y=wk*s[k+i];
s[k]=x+y;
s[k+i]=x-y;
}
}
}
if(op==-){
for(int i=;i<bit;i++){
s[i]=s[i]/(db)bit;
}
}
}
void add(int u,int v){
a[++tot].v=v;
a[tot].next=head[u];
head[u]=tot;
}
void mul(int *ret,int *a,int *b,int n){
for(bit=,bitnum=;bit<=n*;bit<<=)bitnum++;
for(int i=;i<bit;i++){
rev[i]=(rev[i>>]>>)|((i&)<<(bitnum-));
}
for(int i=;i<bit;i++){
A[i]=cp((db)a[i],);
B[i]=cp((db)b[i],);
}
fft(A,);
fft(B,);
for(int i=;i<bit;i++)A[i]=A[i]*B[i];
fft(A,-);
for(int i=;i<bit;i++)ret[i]=(int)(A[i].a+0.5);
}
void getrt(int u,int fa){
mx[u]=;
siz[u]=;
for(int tmp=head[u];tmp!=-;tmp=a[tmp].next){
int v=a[tmp].v;
if(!used[v]&&v!=fa){
getrt(v,u);
siz[u]+=siz[v];
mx[u]=max(mx[u],siz[v]);
}
}
mx[u]=max(mx[u],S-mx[u]);
if(mx[u]<mx[rt])rt=u;
}
void getdep(int u,int fa,int dpt){
mxd=max(mxd,dpt);
dps[dpt]++;
for(int tmp=head[u];tmp!=-;tmp=a[tmp].next){
int v=a[tmp].v;
if(!used[v]&&v!=fa){
getdep(v,u,dpt+);
}
}
}
void divide(int u){
used[u]=true;
num[]=;
for(int tmp=head[u];tmp!=-;tmp=a[tmp].next){
int v=a[tmp].v;
if(!used[v]){
getdep(v,u,);
for(int i=;i<=mxd;i++){
num[i]+=dps[i];
tp[i]=dps[i];
dps[i]=;
}
cnt=max(cnt,mxd);
mul(tp,tp,tp,mxd);
for(int i=;i<=mxd*;i++){
anss[i]-=tp[i];
tp[i]=;
}
mxd=;
}
}
for(int i=;i<=cnt;i++){
tp[i]=num[i];
num[i]=;
}
mul(tp,tp,tp,cnt);
for(int i=;i<=cnt*;i++){
anss[i]+=tp[i];
tp[i]=;
}
cnt=;
for(int tmp=head[u];tmp!=-;tmp=a[tmp].next){
int v=a[tmp].v;
if(!used[v]){
S=siz[v];
rt=;
getrt(v,);
divide(rt);
}
}
}
int main(){
memset(head,-,sizeof(head));
_();
scanf("%d",&n);
jc[]=inv[]=inv[]=;
for(int i=;i<=n+;i++)inv[i]=(ll)(mod-mod/i)*inv[mod%i]%mod;
for(int i=;i<=n+;i++)jc[i]=(ll)jc[i-]*i%mod;
for(int i=;i<n;i++){
scanf("%d%d",&u,&v);
add(u,v);
add(v,u);
}
S=n;
mx[rt=]=;
getrt(,-);
divide(rt);
ans=n;
for(int i=;i<=n;i++){
ans=(ans+(ll)anss[i]*inv[i+]%mod)%mod;
}
printf("%lld",(ll)ans*jc[n]%mod);
return ;
}

【BZOJ3451】Tyvj1953 Normal - 点分治+FFT的更多相关文章

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

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

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

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

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

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

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

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

  5. 3451: Tyvj1953 Normal 点分治 FFT

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

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

    [BZOJ3451]Normal(点分治+FFT) 题面 给你一棵 n个点的树,对这棵树进行随机点分治,每次随机一个点作为分治中心.定义消耗时间为每层分治的子树大小之和,求消耗时间的期望. 分析 根据 ...

  7. BZOJ3451: Tyvj1953 Normal

    题解: 好神的一道题.蒟蒻只能膜拜题解. 考虑a对b的贡献,如果a是a-b路径上第一个删除的点,那么给b贡献1. 所以转化之后就是求sigma(1/dist(i,j)),orz!!! 如果不是分母的话 ...

  8. BZOJ3451 Tyvj1953 Normal 【期望 + 点分治 + NTT】

    题目链接 BZOJ3451 题解 考虑每个点产生的贡献,即为该点在点分树中的深度期望值 由于期望的线性,最后的答案就是每个点贡献之和 对于点对\((i,j)\),考虑\(j\)成为\(i\)祖先的概率 ...

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

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

随机推荐

  1. 2015多校联合训练第一场Tricks Device(hdu5294)

    题意:给一个无向图,给起点s,终点t,求最少拆掉几条边使得s到不了t,最多拆几条边使得s能到t 思路: 先跑一边最短路,记录最短路中最短的边数.总边数-最短边数就是第二个答案 第一个答案就是在最短路里 ...

  2. UVA 10159

    http://blog.csdn.net/metaphysis/article/details/6926997 先向作者表达一下敬佩吧,十分巧妙地利用了状态压缩. 这道题有点组合数学的味道,当一个格子 ...

  3. 微博预计要火一阵的SleepSort之Shell及C实现

    今日在微博看到如此奇妙的代码.竟然还有新的sort算法,对于我这样的渣渣必须研究一下,代码例如以下: #!/bin.bash function f() { sleep "$1" / ...

  4. matlab7安装后的常见问题

    1.有时候.打开MatLab7时,会弹出"找不到指定的模块"对话框,如图(1)所看到的: 图(1) 找不到指定模块 产生这个问题的解决办法是.你的BLAS_VERSION环境变量没 ...

  5. springmvc and maven

    使用Maven构建Spring MVC项目的简单示例 标签: mavenspringmvcspring 2013-09-29 12:40 42823人阅读 评论(8) 收藏 举报  分类: Maven ...

  6. 【视频】零基础学Android开发:蓝牙聊天室APP(一)

    零基础学Android开发:蓝牙聊天室APP第一讲 1. Android介绍与环境搭建:史上最高效Android入门学习 1.1 Google的大小战略 1.2 物联网与云计算 1.3 智能XX设备 ...

  7. JavaScript:Browser 对象

    ylbtech-JavaScript:Browser 对象 1.  Window 对象返回顶部 1. Window 对象 Window 对象 Window 对象表示浏览器中打开的窗口. 如果文档包含框 ...

  8. String不可变性

    今天分析一下String,String有很多实用的特性,比如说“不可变性”,是工程师精心设计的艺术品.用final就是拒绝继承,防止内部属性或方法被破坏. 一,什么是不可变? String不可变很简单 ...

  9. 对JVM还有什么不懂的?一文章带你深入浅出JVM!

    本文跟大家聊聊JVM的内部结构,从组件中的多线程处理,JVM系统线程,局部变量数组等方面进行解析 JVM JVM = 类加载器(classloader) + 执行引擎(execution engine ...

  10. ubuntu刚安装好之后apt-get使用异常

    gaozhang 刚安装好之后,想执行apt-get update 任务,出现以下错误提示   提示说明apt正在执行,我们就野蛮的将apt进程杀.死即可,不过有点多,一个个kill   执行完之后再 ...