[BZOJ3451] Normal 题解

这题分三步:葺网(期望)、淀粉质(点分治)、蓉翅(容斥),再佐以芬芳团(FFT),一道巨难无比的 luogu 黑题就诞生了。

期望

先考虑在淀粉树上,\(i\) 点在 \(j\) 点的子树里的概率。实际上这个问题的每种情况相当于是 \(n\) 个点的各种排列方式。这也就相当于,我们在选择 \(j\) 点之前,没有选择路径 \((i,j)\) 上的其他点,那么 \(j\) 的子树内就会包含 \(i\),否则不会。那么 \(i\) 点在 \(j\) 点子树里的概率就是 \(\dfrac 1{dis(i,j)+1}\)注意,这里的 \(dis(i,j)\) 是边的数量,而不是点的数量

由于期望具有可加性,所以相当于要求 \(\sum\limits_{i=1}^{n}\sum\limits_{j=1}^{n}\dfrac 1{dis(i,j)+1}\)。这玩意可以直接转化为求解 \(num_k=\sum\limits_{i=1}^{n}\sum\limits_{j=1}^{n}[dis(i,j)=k]\)

点分治

此时想到点分治其实水到渠成,关键问题是如何求解。

实际上合并儿子信息的本质就是多项式乘法(因为相当于是两个儿子的信息配对),自然想到 FFT,但是直接把所有都乘到一起正确性会假,用统计“除 \(x\) 子树以外其他子树的信息和”的思想时间复杂度会炸,所以都不行。

容斥

注意力惊人的注意到可以容斥。我们将答案分成两个部分:所有情况(目前考虑的树内的任意两个点可以配对)和错误情况(两个点必须在同一个儿子的子树内),二者相减即为答案。实际上这两个东西都是子树内所有点的深度所得的多项式直接平方的结果。

这样就可以保证在 \(O(m\log m)\) 的时间复杂度内完成合并,加上点分治,时间复杂度为 \(O(n\log^2n)\)

#include<bits/stdc++.h>
#define stp(x) fixed<<setprecision(x)
using namespace std;
const int N=1e5+5;
const long double pi=acos(-1);
namespace FFT{
    struct comn{long double a,b;};
    struct dft{vector<comn>fg;};
    int rev[N],k,mx=1;
    comn operator+(comn x,comn y){
        return {x.a+y.a,x.b+y.b};
    }comn operator-(comn x,comn y){
        return {x.a-y.a,x.b-y.b};
    }comn operator*(comn x,comn y){
        return {x.a*y.a-x.b*y.b,x.b*y.a+x.a*y.b};
    }void operator+=(comn &x,comn y){x=x+y;}
    void operator-=(comn &x,comn y){x=x-y;}
    void operator*=(comn &x,comn y){x=x*y;}
    void init(int n){
        k=0,mx=1;
        while(mx<=n) mx*=2,k++;
        for(int i=0;i<mx;i++)
            rev[i]=(rev[i>>1]>>1)|((i&1)<<(k-1));
    }void fft(dft &a,int n,int fl){
        for(int i=0;i<n;i++)
            if(i<rev[i]) swap(a.fg[i],a.fg[rev[i]]);
        comn om={cos(pi),fl*sin(pi)},w={1,0};
        for(int i=1;i<n;i*=2,om={cos(pi/i),fl*sin(pi/i)})
            for(int j=0;j<n;j+=i*2,w={1,0})
                for(int l=j;l<j+i;l++){
                    comn x=a.fg[l],y=w*a.fg[l+i];
                    a.fg[l]+=y,a.fg[l+i]=x-y,w*=om;
                }
    }void sat(dft &x,int len){
        while(x.fg.size()<len) x.fg.push_back({0,0});
    }void operator+=(dft &x,dft &y){
        sat(x,y.fg.size());
        for(int i=0;i<y.fg.size();i++) x.fg[i]+=y.fg[i];
    }void operator-=(dft &x,dft &y){
        for(int i=0;i<y.fg.size();i++) x.fg[i]-=y.fg[i];
    }void pow2(dft &x){
        int n=x.fg.size();rev[0]=0;
        init(n+n),sat(x,mx),fft(x,mx,1);
        for(int i=0;i<mx;i++) x.fg[i]*=x.fg[i];
        fft(x,mx,-1);
        for(int i=0;i<mx;i++) x.fg[i].a/=mx;
    }
}using namespace FFT;
int n,dep[N],vis[N],sz[N],num[N];
long double ans;dft sum,c,d,al;vector<int>g[N];
void dfsrt(int x,int fa,int sm,int &rt){
    sz[x]=1,num[x]=0;
    for(auto y:g[x]){
        if(y==fa||vis[y]) continue;
        dfsrt(y,x,sm,rt),sz[x]+=sz[y];
        num[x]=max(num[x],sz[y]),sz[y]=1;
    }num[x]=max(num[x],sm-sz[x]);
    if(num[x]<num[rt]) rt=x;
    if(sz[x]==sm) sz[x]=1;
}void dfssz(int x,int fa){
    for(auto y:g[x])
        if(y!=fa&&!vis[y])
            dfssz(y,x),sz[x]+=sz[y];
}void dfsp(int x,int fa,dft &id){
    id.fg[dep[x]=dep[fa]+1].a++;
    for(auto y:g[x])
        if(y!=fa&&!vis[y]) dfsp(y,x,id);
}void solve(int x,int sm){
    if(sm==1) return al.fg[0].a++,void();
    d.fg.clear(),sat(d,sm);int rt=0;
    sum.fg.clear(),sat(sum,sm),sum.fg[0].a=1;
    dfsrt(x,0,sm,rt),dfssz(rt,0),vis[rt]=1,dep[rt]=0;
    for(auto y:g[rt]) if(!vis[y]){
        c.fg.clear(),sat(c,sz[y]+2);
        dfsp(y,rt,c),sum+=c,pow2(c),d+=c;
    }pow2(sum),sum-=d,al+=sum;
    for(auto y:g[rt]) if(!vis[y]) solve(y,sz[y]);
}int main(){
    ios::sync_with_stdio(0);
    cin.tie(0),cout.tie(0);
    cin>>n,num[0]=1e9;
    for(int i=1,x,y;i<n;i++){
        cin>>x>>y,x++,y++;
        g[x].push_back(y);
        g[y].push_back(x);
    }solve(1,n);
    for(int i=0;i<n;i++)
        ans+=al.fg[i].a/(i+1);
    cout<<stp(4)<<ans;
    return 0;
}//fast fourier transform
posted @   长安一片月_22  阅读(6)  评论(0编辑  收藏  举报
编辑推荐:
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· 写一个简单的SQL生成工具
· AI 智能体引爆开源社区「GitHub 热点速览」
· C#/.NET/.NET Core技术前沿周刊 | 第 29 期(2025年3.1-3.9)
点击右上角即可分享
微信分享提示