题目来源: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. HDU多校赛第9场 HDU 4965Fast Matrix Calculation【矩阵运算+数学小知识】

    难度上.,,确实...不算难 问题是有个矩阵运算的优化 题目是说给个N*K的矩阵A给个K*N的矩阵B(1<=N<=1000 && 1=<K<=6),先把他们乘起 ...

  2. 性能优化——mysql数据库

    一 mysql经常使用命令 1. 打开日志 1) show global variables like "%genera%"; 2)set global general_log=o ...

  3. HDU 2457

    直接从root遍历扩展DP,当扩展到的字母和字符串字母相同时,不用修改,不同时,要求修改加1 注意不要扩展危险结点. #include <iostream> #include <cs ...

  4. 输入URL 一瞬间发生了什么

    当你在浏览器中输入url后发生了什么?下面是个人学习过程中的总结,如有理解不正确或不足的地方希望大家指出.先上一张脑图: 还有个问题:www.baidu.com 键入后,域名怎么知道的是这个IP! 补 ...

  5. SQL SEVER 元年是1900年

    用SQL语句求 本月第一天,怎么写? 可以这样写: SELECT DATEADD(mm,DATEDIFF(mm,0,GETDATE()),0); 按照日期函数DATEDIFF的定义,第二个参数是开始日 ...

  6. hdoj--1258--Sum It Up(dfs)

    Sum It Up Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total ...

  7. hdoj--1864--最大保险额(背包)

    最大报销额 Time Limit: 1000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Subm ...

  8. 杂项-SpringEureka:笔记-1

    ylbtech-杂项-SpringEureka:笔记-1 1.返回顶部 1. THE SELF PRESERVATION MODE IS TURNED OFF.THIS MAY NOT PROTECT ...

  9. [JavaEE] IBM - Spring 系列: Spring 框架简介

    Spring AOP 和 IOC 容器入门 在这由三部分组成的介绍 Spring 框架的系列文章的第一期中,将开始学习如何用 Spring 技术构建轻量级的.强壮的 J2EE 应用程序.develop ...

  10. Hdu-6253 2017CCPC-Final K.Knightmare 规律

    题面 题意:给你一个无限大的棋盘,一个象棋中的马,问你这个马,飞n步后,可能的位置有多少种? 题解:看到题,就想先打表试试,于是先写个暴力(枚举每个位置,是马就飞周围8个格子,注意不要在同个循环里把格 ...