CodeChef - PRIMEDST Prime Distance On Tree

题意:

给定一个有 n n n 个结点的树,边权为 1 1 1,随机选取一对结点,问结点间距离为质数的概率。 ( n ≤ 5 × 1 0 4 ) (n \leq 5×10^4) (n5×104)

链接:

https://vjudge.net/problem/CodeChef-PRIMEDST

解题思路:

路径问题,用点分。 c a l cal cal 函数的求解, f f t fft fft 处理出所有路径长度,再判断即可。

参考代码:
#include<bits/stdc++.h>

using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
#define sz(a) ((int)a.size())
#define pb push_back
#define lson (rt << 1)
#define rson (rt << 1 | 1)
#define gmid (l + r >> 1)
const int maxn = 5e5 + 5;
const int inf = 0x3f3f3f3f;
const int mod = 1e9 + 7;

struct FFT{

    struct cp{

        double x, y;
        cp operator + (cp o) { return {x + o.x, y + o.y};}
        cp operator - (cp o) { return {x - o.x, y - o.y};}
        cp operator * (cp o) { return {x * o.x - y * o.y, x * o.y + y * o.x};}
    } w[maxn], a[maxn], b[maxn];
    const double PI = acos(-1.0);
    int rev[maxn], len, n, m;
    void init(ll aa[], int nn, ll bb[], int mm){

        n = nn, m = mm;
        for(len = 1; len <= n + m; len <<= 1);
        for(int i = 0; i < len; ++i) a[i] = b[i] = {0, 0};
        for(int i = 0; i <= n; ++i) a[i].x = aa[i];
        for(int i = 0; i <= m; ++i) b[i].x = bb[i];
        for(int i = 0; i < len / 2; ++i){

            double sita = 2 * PI * i / len;
            double c = cos(sita), s = sin(sita);
            w[i] = {c, s}, w[i + len / 2] = {-c, -s};
        }
        for(int i = 0; i < len; ++i) rev[i] = (i & 1) * (len >> 1) + (rev[i >> 1] >> 1);
    }
    void fft(cp x[], int len, int on){

        for(int i = 0; i < len; ++i) if(i < rev[i]) swap(x[i], x[rev[i]]);
        for(int i = 2; i <= len; i <<= 1){

            int wn = len / i, d = i >> 1;
            for(int j = 0; j < len; j += i){

                for(int k = j, wk = 0; k < j + d; ++k, wk += wn){

                    cp a = x[k], b = w[wk] * x[k + d];
                    x[k] = a + b, x[k + d] = a - b;
                }
            }
        }
        if(on == -1){

            reverse(x + 1, x + len);
            for(int i = 0; i < len; ++i) x[i].x /= len;
        }
    }
    void work(ll c[]){

        fft(a, len, 1), fft(b, len, 1);
        for(int i = 0; i < len; ++i) a[i] = a[i] * b[i];
        fft(a, len, -1);
        for(int i = 0; i <= n + m; ++i) c[i] = (ll)(a[i].x + 0.5);
    }
} fft;
vector<int> G[maxn];
int tag[maxn], siz[maxn], vis[maxn], dis[maxn];
int n, tot, tn, rmn, rt;
ll a[maxn], ans;

void getRt(int u, int f){

    int mx = 0; siz[u] = 1;
    for(auto v : G[u]){

        if(v == f || vis[v]) continue;
        getRt(v, u);
        siz[u] += siz[v];
        mx = max(mx, siz[v]);
    }
    mx = max(mx, tn - siz[u]);
    if(mx < rmn) rmn = mx, rt = u;
}

void dfs(int u, int f, int d){

    dis[++tot] = d;
    for(auto v : G[u]){

        if(v == f || vis[v]) continue;
        dfs(v, u, d + 1);
    }
}

void cal(int u, int d, int flg){

    tot = 0;
    dfs(u, 0, d);
    int mx = *max_element(dis + 1, dis + 1 + tot);
    for(int i = 0; i <= mx; ++i) a[i] = 0;
    // cout << u << " " << d << " " << flg << " " << tot << " " << mx << " ?? " << endl;
    for(int i = 1; i <= tot; ++i) ++a[dis[i]];
    // for(int i = 0; i <= mx; ++i) cout << i << " xx " << a[i] <<endl;
    fft.init(a, mx, a, mx);
    fft.work(a);
    // for(int i = 0; i <= mx + mx; ++i) cout << i << " yy " << a[i] << endl;
    for(int i = 0; i <= mx + mx; ++i) ans += flg * a[i] * !tag[i];
    // cout << ans << " xxxxx " << endl;
    // cout << "------" << endl;
}

void dfz(int u){

    vis[u] = 1;
    cal(u, 0, 1);
    for(auto v : G[u]){

        if(vis[v]) continue;
        cal(v, 1, -1);
        tn = siz[v], rmn = inf, getRt(v, u);
        dfz(rt);
    }
    vis[u] = 0;
}

int main(){

    ios::sync_with_stdio(0); cin.tie(0);
    cin >> n;
    tag[0] = tag[1] = 1;
    for(int i = 2; i <= n + n; ++i){

        if(!tag[i]){

            for(int j = i + i; j <= n + n; j += i) tag[j] = 1;
        }
    }
    for(int i = 1; i < n; ++i){

        int u, v; cin >> u >> v;
        G[u].pb(v), G[v].pb(u);
    }
    tn = n, rmn = inf, getRt(1, 0);
    dfz(rt);
    ll tot = n * 1ll * (n - 1);
    // cout << tot << " " << ans << endl;
    cout << ((double)ans / tot) << endl;
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值