题目来源: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. 0926mysql中MRR的用法

    转自 http://blog.itpub.net/22664653/viewspace-1673682  [MySQL]MySQL5.6新特性之Multi-Range Read 2015-05-27 ...

  2. 洛谷——P1095 守望者的逃离

    https://www.luogu.org/problem/show?pid=1095#sub 题目描述 恶魔猎手尤迪安野心勃勃,他背叛了暗夜精灵,率领深藏在海底的娜迦族企图叛变.守望者在与尤迪安的交 ...

  3. 微信企业号开发:UserAgent

    userAgent 属性是一个仅仅读的字符串,声明了浏览器用于 HTTP 请求的用户代理头 的值.微信企业号的打开网页的userAgent又包括那些信息呢? 使用userAgent能够推断用户訪问的浏 ...

  4. Java缓存server调优

    搜索降级方案中xmn開始使用bizer默认的128M,很慢. 偶然改成1G,效果立刻上来,可是xmx调大并没有明显效果.                  100并发             200并 ...

  5. Node.js:文件系统

    ylbtech-Node.js:文件系统 1.返回顶部 1. Node.js 文件系统 Node.js 提供一组类似 UNIX(POSIX)标准的文件操作API. Node 导入文件系统模块(fs)语 ...

  6. 杂项:hive(数据仓库工具)

    ylbtech-杂项:hive(数据仓库工具) hive是基于Hadoop的一个数据仓库工具,可以将结构化的数据文件映射为一张数据库表,并提供简单的sql查询功能,可以将sql语句转换为MapRedu ...

  7. Python笔记(十一)——数据抓取例子

    上班时候想看股票行情怎么办?试试这个小例子,5分钟拉去一次股票价格,预警: #coding=utf-8 import re import urllib2 import time import thre ...

  8. BZOJ 2729 高精度+组合数学

    思路: 考虑 把男生排成一排 女生和老师往里插 分成两种情况. 1. 女生中间夹着老师 2. 女生中间没有夹着老师 求一下组合* 阶乘就好了 先放Python代码 简洁易懂 def fact(n): ...

  9. web前端处理订单待支付倒计时计算显示问题

    在商城类项目的时候,有很多待支付的订单,有时候在订单列表页面会分别显示倒计时,就是页面会有很多倒计时的订单. 处理方法: 1.调用后端接口拿到所有的订单,获取所有的倒计时订单,获取到期时间(尽量时间戳 ...

  10. 【转】PowerDesigner物理数据表生成C#实体类Model

    model实体类是什么: 在三层架构UI,BLL,DAL中,有时用户插入一条记录到数据库中,必然会有不少数据,按正常编程,也必然会一下子调用某个函数传入不少参数.为了减少参数的数量,达到高效简洁的效果 ...