[分治][Trie][prufer数列] 51Nod 1601 完全图的最小生成树计数

Solution S o l u t i o n

考虑从高位到低位枚举。
显然是把相同的放在一边,不同的找到权值最小的连边,这个可以 Trie Trie
这样分治下去,最后得到一个没有限制的联通块,贡献就是 sizesize2 s i z e s i z e − 2
因为偷懒写了 vector vector ,还必须得预处理 nn2 n n − 2 这样的东西qwq。。跑得巨慢。。
复杂度是 O(nlog2w) O ( n log 2 ⁡ w )
如果 Trie Trie 和分治一起写的话可能是可以 O(nlogw) O ( n log ⁡ w ) 的啦。。

#include <bits/stdc++.h>
#define show(x) cerr << #x << " = " << x << endl
using namespace std;

typedef long long ll;
typedef pair<int, int> pairs;
const int N = 101010;
const int M = 3030303;
const int K = 30;
const int MOD = 1000000007;

inline char get(void) {
    static char buf[100000], *S = buf, *T = buf;
    if (S == T) {
        T = (S = buf) + fread(buf, 1, 100000, stdin);
        if (S == T) return EOF;
    }
    return *S++;
}
template<typename T>
inline void read(T &x) {
    static char c; x = 0; int sgn = 0;
    for (c = get(); c < '0' || c > '9'; c = get()) if (c == '-') sgn = 1;
    for (; c >= '0' && c <= '9'; c = get()) x = x * 10 + c - '0';
    if (sgn) x = -x;
}

int ch[M][2];
int sz[M];
int tcnt, root;
ll val;
int cnt;
int n;
int pwn[N];
vector<int> a;

inline pairs operator ^(int x, pairs y) {
    return pairs(x ^ y.first, y.second);
}

inline int pwr(int a, int b) {
    int c = 1;
    while (b) {
        if (b & 1) c = (ll)c * a % MOD;
        b >>= 1; a = (ll)a * a % MOD;
    }
    return c;
}
inline void init(void) {
    tcnt = root = 1;
}
inline void insert(int u, int x) {
    ++sz[u];
    for (int i = K; ~i; i--) {
        int &v = ch[u][x >> i & 1];
        if (v == 0) v = ++tcnt;
        u = v; ++sz[u];
    }
}
inline pairs query(int u, int x, int d) {
    if (d < 0) return pairs(0, sz[u]);
    int y = x >> d & 1;
    if (ch[u][y]) return query(ch[u][y], x, d - 1);
    return (1 << d) ^ query(ch[u][y ^ 1], x, d - 1);
}
inline void watch(vector<int> x) {
    for (int u: x) cerr << u << ' ';
    cerr << endl;
}
inline void divAndConq(int u, vector<int> &b, int d) {
    vector<int> b0, b1;
    if (u == 0) return;
    if (d < 0) {
        int n = b.size();
        if (n >= 2) cnt = (ll)cnt * pwn[n] % MOD;
        return;
    }
    b0.clear(); b1.clear();
    for (int x: b)
        if (x >> d & 1) b1.push_back(x);
        else b0.push_back(x);
    //watch(b0); watch(b1);
    //cerr << b0.size() << ' ' << b1.size() << endl;
    ll w = 1ll << 60, c;
    if (b0.size() < b1.size()) {
        if (b0.size() == 0) w = 0, c = 1;
        for (int x: b0) {
            pairs res = (1 << d) ^ query(ch[u][1], x, d - 1);
            if (res.first == w) {
                c += res.second;
            } else if (res.first < w) {
                w = res.first;
                c = res.second;
            }
        }
    } else {
        if (b1.size() == 0) w = 0, c = 1;
        for (int x: b1) {
            pairs res = (1 << d) ^ query(ch[u][0], x, d - 1);
            if (res.first == w) {
                c += res.second;
            } else if (res.first < w) {
                w = res.first;
                c = res.second;
            }
        }
    }
    val += w; cnt = (ll)cnt * c % MOD;
    divAndConq(ch[u][0], b0, d - 1);
    divAndConq(ch[u][1], b1, d - 1);
}

int main(void) {
    freopen("1.in", "r", stdin);
    freopen("1.out", "w", stdout);
    read(n); a.resize(n); init();
    for (int i = 2; i <= n; i++)
        pwn[i] = pow(i, i - 2);
    val = 0; cnt = 1;
    for (int i = 0; i < n; i++) {
        read(a[i]);
        insert(root, a[i]);
    }
    divAndConq(root, a, K);
    printf("%lld\n%d\n", val, cnt);
    return 0;
}
    原文作者:Trie树
    原文地址: https://blog.csdn.net/Vectorxj/article/details/79477036
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞