你最愿意做的哪件事,才是你的天赋所在

0%

点分治+FFT-CodeChef-PRIMEDST

题目链接

CodeChef-PRIMEDST

思路

采用点分治统计所有的路径,但是如果是朴素的路径相乘复杂度是O(n^2),所以采用FFT加速多项式乘法达到(nlogn)的复杂度,总体复杂度就是(nlognlogn)的复杂度。
做题步骤:

  1. 点分治处理
  2. 统计当前子树的路径
  3. 进行FFT
  4. 累加路径,放进桶里
  5. 重复重心的所有子树重复2,3,4步骤
  6. 清空桶,继续点分治
  7. 统计答案

    代码实现

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    #include<bits/stdc++.h>
    using namespace std;
    #define ll long long
    #define pi acos(-1)
    const int N = 1e5+10;
    int head[N],tot;
    ll sum,rt,dis[N],tmp[N<<4],maxp[N],siz[N],len,d[N],up,dn;
    int f[N<<2],g[N<<2];
    int prime[N];
    bool isprime[N],pcnt;
    ll ans[N];
    int cnt,n;
    bool vis[N];
    int r[N];
    void init(){
    isprime[0]=isprime[1]=1;
    for(int i=2;i<N;i++){
    if(!isprime[i]){
    for(int j=i+i;j<N;j+=i){
    isprime[j]=1;
    }
    }
    }
    }
    struct cp
    {
    double r,i;
    cp(double _r = 0,double _i = 0)
    {
    r = _r; i = _i;
    }
    cp operator +(const cp &b)
    {
    return cp(r+b.r,i+b.i);
    }
    cp operator -(const cp &b)
    {
    return cp(r-b.r,i-b.i);
    }
    cp operator *(const cp &b)
    {
    return cp(r*b.r-i*b.i,r*b.i+i*b.r);
    }
    }F[N<<2],G[N<<2],H[N<<2];
    void FFT(cp *s,int n,int inv){
    int bit=0;
    while ((1<<bit)<n)bit++;
    for(int i=0;i<n;i++)r[i]=i;
    for(int i=0;i<n;i++)r[i]=(r[i>>1]>>1)|((i&1)<<(bit-1));
    for(int i=0;i<n;i++){
    if(i<r[i])swap(s[i],s[r[i]]);
    }
    for(int len=2;len<=n;len*=2){
    cp wn = cp(cos(pi*2.0/len),inv*sin(pi*2.0/len));
    for(int st=0;st<n;st+=len){
    cp w = cp(1.0,0.0);
    for(int i=st;i<st+len/2;i++,w=w*wn){
    cp x=s[i];//偶次幂
    cp y=w*s[i+len/2];//奇次幂
    s[i]=x+y;
    s[i+len/2]=x-y;
    }
    }
    }
    }
    struct edge{
    int v,w,nex;
    edge() {}
    edge(int v, int w, int nex) : v(v), w(w), nex(nex) {}
    }edges[N<<1];
    void add(int u,int v,int w){
    edges[tot]=edge(v,w,head[u]);
    head[u]=tot++;
    }
    void getrt(int u,int fa){
    maxp[u]=0,siz[u]=1;
    for(int i=head[u];~i;i=edges[i].nex){
    int v = edges[i].v;
    if(vis[v]||v==fa)continue;
    getrt(v,u);
    siz[u]+=siz[v];
    if(maxp[u]<siz[v])maxp[u]=siz[v];
    }
    maxp[u]=max(maxp[u],sum-siz[u]);
    if(maxp[u]<maxp[rt])rt=u;
    }
    void getdis(int u,int fa){
    tmp[cnt++]=dis[u];
    for(int i=head[u];~i;i=edges[i].nex){
    int v =edges[i].v;
    if(vis[v]||v==fa)continue;
    dis[v]=dis[u]+edges[i].w;
    getdis(v,u);
    }
    }
    void getdeep(int u,int fa,int deep,int &mx){
    mx = max(mx,deep);
    for(int i=head[u];~i;i=edges[i].nex){
    int v = edges[i].v;
    if(vis[v]||v==fa)continue;
    getdeep(v,u,deep+1,mx);
    }
    }
    void solve(int u){
    int mx = -1;
    getdeep(u,0,0,mx);
    len = 1;
    while(len<=2*mx)len<<=1;
    for(int i=head[u];~i;i=edges[i].nex){
    int v = edges[i].v;
    if(vis[v])continue;
    dis[v]=edges[i].w;
    cnt = 0;
    getdis(v,u);
    for(int j=0;j<cnt;j++){
    g[tmp[j]]++;
    if(!isprime[tmp[j]])up++;
    }

    int bit=0;
    while ((1<<bit)<len)bit++;
    for(int j=0;j<len;j++){
    G[j].r=g[j],G[j].i=0;
    F[j].r=f[j],F[j].i=0;
    }
    FFT(F,len,1);
    FFT(G,len,1);
    for(int j=0;j<len;j++)H[j]=F[j]*G[j];
    FFT(H,len,-1);
    for(int j=0;j<len;j++){
    if(!isprime[j])
    up+=(ll)(H[j].r/len+0.5);
    }
    for(int j=0;j<cnt;j++)f[tmp[j]]++,g[tmp[j]]=0;
    }
    for(int i=0;i<=n;i++)f[i]=0;
    }
    void divide(int u){
    vis[u]=1;
    solve(u);
    for(int i=head[u];~i;i=edges[i].nex){
    int v = edges[i].v;
    if(vis[v])continue;
    maxp[rt=0]=sum=siz[v];
    getrt(v,0);
    getrt(rt,0);
    divide(rt);
    }
    }
    int main(){
    init();
    memset(head,-1,sizeof(head));
    scanf("%d",&n);
    for(int i=1;i<n;i++){
    int u,v;
    scanf("%d %d",&u,&v);
    add(u,v,1);
    add(v,u,1);
    }
    maxp[0]=sum=n;
    getrt(1,0);
    getrt(rt,0);
    divide(rt);
    dn = 1ll*n*(n-1)/2;
    printf("%.6lf\n",(double)up/(double)dn);
    }
-------------你最愿意做的哪件事才是你的天赋所在-------------