题意:询问在一棵树上任选两点[tex]a,b[/tex]满足[tex]dist(a,b)[/tex]是质数的概率,边权均为1。
我们考虑统计树上点对路径为k的点对个数cnt[k]。
显然就是点分。
设一颗子树的dis[k]表示从根下来的路径为k的节点数。
设子树的深度为L,子树都按L从小到大排序,再合并信息,这样复杂度有保证。
在搞第i棵子树的时候,设前i-1棵子树的dis数组为a,第i棵子树的dis数组为b。
一开始只要a[0]=1即可。
统计时,[tex]\text{cnt}[k]=\sum_{i=0}^k \text{a}[i]\text{b}[k-i][/tex]。
然后把b数组加进a数组即可。
暴力统计显然还不如不点分直接暴力= =。
使用FFT,复杂度为[tex]\text{O}(n\text{log}^{2}n)[/tex]。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
const int maxn=50010,maxm=100020;
const double pi=3.1415926535897932384626;
int p[maxm],n1[maxm],h[maxn],sz[maxn],vis[maxn]={0},ee=0,n;
int size,root,mincnt,maxdis,d[maxn];
long long cnt[maxn],allcnt[maxn],f[maxn],ans;
int isprime[maxn]={0},prime[maxn],pcnt=0,flag[maxn]={0},tmp[100];
#define pr pair<int,int>
#define X first
#define Y second
#define mp make_pair
pr son[maxn];
struct FFT{
struct cpx{
double a,b;
cpx operator + (const cpx&o)const{
return (cpx){a+o.a,b+o.b};
}
cpx operator - (const cpx&o)const{
return (cpx){a-o.a,b-o.b};
}
cpx operator * (const cpx&o)const{
return (cpx){a*o.a-b*o.b,b*o.a+a*o.b};
}
}c[maxn*4],d[maxn*4];
int len;
int rev(int x){
tmp[0]=0;
for(;x;x>>=1) tmp[++tmp[0]]=x&1;
int t=0;
for(int i=1;i<=tmp[0];i++) if(tmp[i]) t+=1<<(len-i);
return t;
}
void fft(cpx *A,int n,int fl=1){
for(int i=0;i<n;i++){
int t=rev(i);
if(i<t) swap(A[i],A[t]);
}
for(int m=2;m<=n;m<<=1){
cpx w=(cpx){cos(fl*2*pi/(double)m),sin(fl*2*pi/(double)m)};
for(int k=0;k<n;k+=m){
cpx cur=(cpx){1,0};
for(int j=k;j<k+(m>>1);j++,cur=cur*w){
cpx t=cur*A[j+(m>>1)],u=A[j];
A[j]=u+t;
A[j+(m>>1)]=u-t;
}
}
}
}
long long C[maxn*4],D[maxn*4],ans[maxn*4];
void merge(int L){
len=0;
while((1<<len)<2*L) len++;
for(int i=0;i<(1<<len);i++){
if(i<=L){
c[i]=(cpx){(double)C[i],0};
d[i]=(cpx){(double)D[i],0};
}else{
c[i]=(cpx){0,0};
d[i]=(cpx){0,0};
}
}
fft(c,1<<len);
fft(d,1<<len);
for(int i=0;i<(1<<len);i++) c[i]=c[i]*d[i];
fft(c,1<<len,-1);
for(int i=0;i<(1<<len);i++) ans[i]=(long long)((double)c[i].a/(double)(1<<len)+0.5);
}
}F;
void pre(int n){
for(int i=2;i<=n;i++){
if(!flag[i]) isprime[i]=1,prime[++pcnt]=i;
for(int j=1;j<=pcnt && i*prime[j]<=n;j++){
flag[i*prime[j]]=1;
if(i%prime[j]==0) break;
}
}
}
void ae(int x,int y){
p[ee]=y; n1[ee]=h[x]; h[x]=ee++;
p[ee]=x; n1[ee]=h[y]; h[y]=ee++;
}
void findroot(int u,int fa){
sz[u]=1;
int mx=0;
for(int i=h[u];~i;i=n1[i]){
if(vis[p[i]] || p[i]==fa) continue;
findroot(p[i],u);
sz[u]+=sz[p[i]];
mx=max(sz[p[i]],mx);
}
mx=max(mx,size-sz[u]);
if(mx<mincnt){
mincnt=mx;
root=u;
}
}
void dfs(int u,int fa){
cnt[d[u]]++;
maxdis=max(maxdis,d[u]);
for(int i=h[u];~i;i=n1[i]){
if(p[i]==fa || vis[p[i]]) continue;
d[p[i]]=d[u]+1;
dfs(p[i],u);
}
}
void merge(int l){
for(int i=0;i<=l;i++) F.C[i]=allcnt[i],F.D[i]=cnt[i];
F.merge(l);
for(int i=1;i<=l;i++) allcnt[i]+=cnt[i];
for(int i=1;i<=l;i++) if(isprime[i]) ans+=F.ans[i];
}
void solve(int u){
int scnt=0;
for(int i=h[u];~i;i=n1[i]){
if(vis[p[i]]) continue;
d[p[i]]=1;maxdis=0;
dfs(p[i],u);
son[++scnt]=mp(maxdis,p[i]);
}
sort(son+1,son+scnt+1);
memset(allcnt,0,sizeof(allcnt));
allcnt[0]=1;
son[0].X=1;
for(int i=1;i<=scnt;i++){
memset(cnt,0,sizeof(cnt));
d[son[i].Y]=1;
dfs(son[i].Y,u);
merge(son[i-1].X+son[i].X);
}
vis[u]=1;
findroot(u,0);
for(int i=h[u];~i;i=n1[i]){
if(vis[p[i]]) continue;
mincnt=size=sz[p[i]];
findroot(p[i],u);
solve(root);
}
}
int main(){
scanf("%d",&n);
pre(n);
int x,y;
memset(h,-1,sizeof(h));
for(int i=1;i<n;i++){
scanf("%d%d",&x,&y);
ae(x,y);
}
mincnt=size=n;
findroot(1,0);
solve(root);
long long all=1ll*n*(n-1)/2;
printf("%.8lf\n",(double)ans/(double)all);
return 0;
}