题意
边长为1,求长度为素数的路径数。
思路
路径计数:点分治+fft
按深度为下标,次数为值卷起来。
结果会吧两端相同的路径算一次,把两端不同的路径算两次。
因此枚举每个点吧对应深度下标减一。
当然这是那种需要容斥的点分治。
code
点击查看代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double db;
const db pi=acos(-1);
const int N=1e6+5;
int rev[N],up,l,nxt[N],to[N],head[N],ecnt,n,smx[N],rt;
int p[N],ptot;
bool is_p[N];
ll ans;
void add_edge(int u,int v) {nxt[++ecnt]=head[u];to[ecnt]=v;head[u]=ecnt;}
struct cop {
db img,rl;
friend inline cop operator*(cop x,cop y) {return (cop){x.img*y.rl+x.rl*y.img,x.rl*y.rl-x.img*y.img};}
friend inline cop operator+(cop x,cop y) {return (cop){x.img+y.img,x.rl+y.rl};}
friend inline cop operator-(cop x,cop y) {return (cop){x.img-y.img,x.rl-y.rl};}
}f[N],g[N];
void FFT(cop *a,int op) {
for(int i=0;i<up;i++) {
if(rev[i]>i)swap(a[i],a[rev[i]]);
}
for(int mid=1;mid<up;mid<<=1) {
cop w1=(cop){op*sin(pi/mid),cos(pi/mid)};
for(int len=mid<<1,l=0;l<up;l+=len) {
cop W=(cop){0,1};
for(int i=0;i<mid;i++,W=W*w1) {
int p=l+i,q=p+mid;
cop x=a[p],y=W*a[q];
a[p]=x+y;a[q]=x-y;
}
}
}
}
void Mul() {
FFT(f,-1);
for(int i=0;i<up;i++) f[i]=f[i]*f[i];
FFT(f,1);
for(int i=0;i<up;i++) f[i].rl=(ll)(f[i].rl/up+0.5);
}
void _xxs() {
for(int i=2;i<=n;i++) {
if(!is_p[i]) {p[++ptot]=i;}
for(int j=1,x;j<=ptot&&(x=p[j]*i)<=n;j++) {
is_p[x]=1;
if(i%p[j]==0)break;
}
}
}
void gt_up(int len) {
up=1;l=0;
while(up<=len) {up<<=1,l++;}
for(int i=1;i<up;i++)rev[i]=(rev[i>>1]>>1)|((i&1)<<(l-1));
}
bool vis[N];
int sz[N];
void gt_sz(int u,int fa) {
sz[u]=1;
for(int i=head[u];i;i=nxt[i]) {
int v=to[i];if(v==fa||vis[v])continue;
gt_sz(v,u);sz[u]+=sz[v];
}
}
void gt_rt(int u,int fa,int tot) {
smx[u]=0;
for(int i=head[u];i;i=nxt[i]) {
int v=to[i]; if(v==fa||vis[v])continue;
gt_rt(v,u,tot);
smx[u]=max(smx[u],sz[v]);
}
smx[u]=max(smx[u],tot-sz[u]);
if(smx[u]<smx[rt]) rt=u;
}
int mxd,cnt[N];
void dfs(int u,int fa,int dep) {
cnt[dep]++;mxd=max(mxd,dep);
// printf("dep[%d]=%d\n",u,dep);
for(int i=head[u];i;i=nxt[i]) {
int v=to[i];if(v==fa||vis[v])continue;
dfs(v,u,dep+1);
}
}
ll calc(int u,ll dep) { //经过u的素数路径数量
mxd=0;dfs(u,0,dep);
gt_up((mxd<<1)+1);
for(int i=0;i<up;i++) {
f[i]=(cop){0,cnt[i]};
}
Mul();
ll res=0;
for(int i=0;i<=mxd;i++) {f[i<<1].rl-=cnt[i];}
for(int i=1;p[i]<=(mxd<<1)&&i<=ptot;i++) {
res+=f[p[i]].rl;
}
for(int i=0;i<=mxd;i++) cnt[i]=0;
return res/2;
}
void Divide(int u) {
rt=0,gt_rt(u,0,sz[u]);
vis[u=rt]=1;
gt_sz(u,0);
// ll res=calc(u,0);
ans+=calc(u,0);
for(int i=head[u];i;i=nxt[i]) {
int v=to[i];if(vis[v])continue;
// res-=calc(v,1);
ans-=calc(v,1);
Divide(v);
}
// for(int i=head[u];i;i=nxt[i]) {
// int v=to[i];if(vis[v])continue;
// Divide(v);
// }
// printf("sz[%d]=%d\n",u,sz[u]);
// printf("res=%lld\n",res);
}
int main() {
smx[0]=1e9;
scanf("%d",&n);
_xxs();
for(int i=1;i<n;i++) {
int u,v;scanf("%d%d",&u,&v);
add_edge(u,v),add_edge(v,u);
}
gt_sz(1,0);Divide(1);
// printf("%lld\n",ans);
printf("%.6lf\n",ans*2.0/n/(n-1));
return 0;
}