标签:algo break 统计 swap prime fft 关于 pre 就是
题意:求在树上任选一条路径,长度为质数的概率
直接点分,不难,之所以写是因为想记一下关于时间复杂度的东西...
我们采用按顺序加子树的方式统计答案,记一个深度有多少个点,卷积统计答案
如果当前节点的子树最深深度分别为$d_{1\cdots m}$,那么我们合并$i$时的数据规模为$\max\{d_{1\cdots i-1}\}+d_i$,如果直接做可以被卡到$n^2$
所以我们把$d_i$从小到大排序,这样做的总数据规模就是$\sum d_i$级别的,也就是不超过当前子树大小,符合点分的要求
#include<stdio.h>
#include<math.h>
#include<string.h>
#include<algorithm>
using namespace std;
typedef long long ll;
typedef double du;
const int T=50000,inf=2147483647;
const du pi=3.141592653589793238462643383;
int pr[T+10];
bool np[T+10];
void sieve(){
int i,j,M=0;
np[1]=1;
for(i=2;i<=T;i++){
if(!np[i])pr[++M]=i;
for(j=1;j<=M&&i*pr[j]<=T;j++){
np[i*pr[j]]=1;
if(i%pr[j]==0)break;
}
}
}
struct comp{
du x,y;
comp(du a=0,du b=0){x=a;y=b;}
};
comp operator+(comp a,comp b){return comp(a.x+b.x,a.y+b.y);}
comp operator-(comp a,comp b){return comp(a.x-b.x,a.y-b.y);}
comp operator*(comp a,comp b){return comp(a.x*b.x-a.y*b.y,a.x*b.y+a.y*b.x);}
int rev[65536],N;
comp w[16][65536];
void pre(int n){
int i,j,k;
for(N=1,k=0;N<=n;N<<=1)k++;
for(i=0;i<N;i++)rev[i]=(rev[i>>1]>>1)|((i&1)<<(k-1));
for(i=2,k=0;i<=N;i<<=1,k++){
for(j=0;j<i>>1;j++)w[k][j]=comp(cos(j*pi/(i/2)),sin(j*pi/(i/2)));
}
}
void fft(comp*a,int on){
int i,j,k,f;
comp t;
for(i=0;i<N;i++){
if(i<rev[i])swap(a[i],a[rev[i]]);
}
for(i=2,f=0;i<=N;i<<=1,f++){
for(j=0;j<N;j+=i){
for(k=0;k<i>>1;k++){
t=w[f][k];
t.y*=on;
t=t*a[i/2+j+k];
a[i/2+j+k]=a[j+k]-t;
a[j+k]=a[j+k]+t;
}
}
}
if(on==-1){
for(i=0;i<N;i++)a[i].x/=N;
}
}
int h[50010],nex[100010],to[100010],M;
void add(int a,int b){
M++;
to[M]=b;
nex[M]=h[a];
h[a]=M;
}
bool v[50010];
int siz[50010];
void dfs1(int fa,int x){
siz[x]=1;
for(int i=h[x];i;i=nex[i]){
if(!v[to[i]]&&to[i]!=fa){
dfs1(x,to[i]);
siz[x]+=siz[to[i]];
}
}
}
int mn,cn,n;
void dfs2(int fa,int x){
int i,mx=0;
for(i=h[x];i;i=nex[i]){
if(!v[to[i]]&&to[i]!=fa){
dfs2(x,to[i]);
mx=max(mx,siz[to[i]]);
}
}
mx=max(mx,n-siz[x]);
if(mx<mn){
mn=mx;
cn=x;
}
}
int md[50010];
void dfs3(int fa,int x){
md[x]=1;
for(int i=h[x];i;i=nex[i]){
if(!v[to[i]]&&to[i]!=fa){
dfs3(x,to[i]);
md[x]=max(md[x],md[to[i]]+1);
}
}
}
int p[50010],d[50010],td[50010];
void dfs4(int fa,int x,int d){
td[d]++;
for(int i=h[x];i;i=nex[i]){
if(!v[to[i]]&&to[i]!=fa)dfs4(x,to[i],d+1);
}
}
bool cmp(int x,int y){return md[x]<md[y];}
ll ans;
comp A[65536],B[65536];
void solve(int x){
int i,j,M;
dfs1(0,x);
n=siz[x];
mn=inf;
dfs2(0,x);
x=cn;
v[x]=1;
dfs3(0,x);
M=0;
for(i=h[x];i;i=nex[i]){
if(!v[to[i]])p[++M]=to[i];
}
if(M==0)return;
sort(p+1,p+M+1,cmp);
memset(d,0,(md[p[M]]+1)<<2);
d[0]=1;
for(i=1;i<=M;i++){
memset(td,0,(md[p[i]]+1)<<2);
dfs4(x,p[i],1);
pre(md[p[i-1]]+md[p[i]]);
memset(A,0,N<<4);
memset(B,0,N<<4);
for(j=0;j<=md[p[i-1]];j++)A[j]=d[j];
for(j=0;j<=md[p[i]];j++)B[j]=td[j];
fft(A,1);
fft(B,1);
for(j=0;j<N;j++)A[j]=A[j]*B[j];
fft(A,-1);
for(j=1;j<=md[p[i-1]]+md[p[i]];j++){
if(!np[j])ans+=llround(A[j].x);
}
for(j=0;j<=md[p[i]];j++)d[j]+=td[j];
}
for(i=h[x];i;i=nex[i]){
if(!v[to[i]])solve(to[i]);
}
}
int main(){
sieve();
int n,i,x,y;
scanf("%d",&n);
for(i=1;i<n;i++){
scanf("%d%d",&x,&y);
add(x,y);
add(y,x);
}
solve(1);
printf("%.7lf",ans/(double)((ll)n*(n-1)/2));
}
CC昨晚不给提交,今早又复活了...
标签:algo break 统计 swap prime fft 关于 pre 就是
原文地址:https://www.cnblogs.com/jefflyy/p/9602429.html