题意

边长为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;
}