更好的瀏覽體驗 Press Here
Problem
題目大意:
有 個點,每個點權值爲 ,兩個點連邊費用爲 ,問 最小生成樹的邊權和 方案數
Solution
最小生成樹麼,一般使用 Kruskal 算法
但是在這裏,由於邊數達到了 的級別,顯然是不能直接排序的
但是隻要抓住 Kruskal 的精髓:邊權從低到高合併
那麼考慮一下如何找出邊權最低的點對:按照二進制位從低到高合併
每個點的二進制狀態我們可以用一顆 Trie 來維護,顯然其 LCA 越深其異或值越小
所以將 Trie 從低到高合併的過程是這樣的
Kruskal(T):
T 右子樹非空 Kruskal(T 右子樹)
T 左子樹非空 Kruskal(T 左子樹)
//這樣就形成了兩個聯通塊,只需要在兩個塊內選擇一條邊相連即可
若左右子樹均非空
在左右子樹中找出異或值最小點對
計算方案數
最小生成樹 邊權+ 方案數*
找異或值最小點對過程如下
cal(x , y):
x 的左子樹非空 && y 的左子樹非空 cal(x 的左子樹 , y 的左子樹)
x 的右子樹非空 && y 的右子樹非空 cal(x 的右子樹 , y 的右子樹)
若沒有相同的子樹(左左/右右)
x 的左子樹非空 cal(x 的左子樹 , y 的右子樹)
x 的右子樹非空 cal(x 的右子樹 , y 的左子樹)
返回最小異或值,方案數
這樣利用其左右子樹獨立的性質就可以很簡單的解決這道題
分析
這個題目的複雜度很多人認爲就是
但是真的是這樣麼
一棵滿二叉樹能夠使得每次計算最小點對時訪問到所有兒子節點
滿二叉樹一共有 層,這是由節點個數所限制的,而鏈的長度爲
所以當滿二叉樹的所有葉子節點下方都掛着一條長爲 的鏈時,達到最大複雜度
這時,滿二叉樹的每個節點被訪問 次,鏈上每個節點被訪問 次
總複雜度爲
代碼
#include <bits/stdc++.h>
using namespace std;
const int N = 1000010;
const int mod = 1000000007;
typedef long long ll;
int s[N << 5][2];
int c[N << 5];
int n , cnt = 1 , m1 , c1;
long long ans1 = 1 , ans2;
int read() {
int ans = 0 , flag = 1;
char ch = getchar();
while(ch > '9' || ch < '0') {if(ch == '-') flag = -1; ch = getchar();}
while(ch <= '9' && ch >= '0') {ans = ans * 10 + ch - '0'; ch = getchar();}
return ans * flag;
}
int qpow(int a , int b) {
int ans = 1;
while(b) {
if(b & 1) ans = 1ll * ans * a % mod;
a = 1ll * a * a % mod;
b >>= 1;
}
return ans;
}
void insert(int x) {
int now = 1;
for(int i = 29 ; ~ i ; -- i) {
if(!s[now][(x >> i) & 1]) s[now][(x >> i) & 1] = ++ cnt;
now = s[now][(x >> i) & 1];
}
++ c[now];
}
void get_min(int x , int y , int d , int v = 0) {
if(d < 0) {
if(v < m1) {m1 = v; c1 = 1ll * c[x] * c[y] % mod;}
else if(v == m1) {c1 = (1ll * c1 + 1ll * c[x] * c[y] % mod) % mod;}
}
if(s[x][0] && s[y][0]) {
get_min(s[x][0] , s[y][0] , d - 1 , v);
if(s[x][1] && s[y][1]) get_min(s[x][1] , s[y][1] , d - 1 , v);
}
else if(s[x][1] && s[y][1]) get_min(s[x][1] , s[y][1] , d - 1 , v);
else {
if(s[x][0]) get_min(s[x][0] , s[y][1] , d - 1 , v + (1 << d));
if(s[x][1]) get_min(s[x][1] , s[y][0] , d - 1 , v + (1 << d));
}
}
void solve(int x , int d) {
if(d < 0) {
if(c[x] > 2) ans1 = ans1 * qpow(c[x] , c[x] - 2) % mod;
return;
}
if(!s[x][0]) solve(s[x][1] , d - 1);
else if(!s[x][1]) solve(s[x][0] , d - 1);
else {
solve(s[x][0] , d - 1);
solve(s[x][1] , d - 1);
m1 = 1 << 30 , c1 = 0;
get_min(s[x][0] , s[x][1] , d - 1);
ans2 = ans2 + (1ll << d) + m1;
ans1 = ans1 * c1 % mod;
}
}
int main() {
n = read();
for(int i = 1 ; i <= n ; ++ i) insert(read());
solve(1 , 29);
printf("%lld\n%lld\n", ans2 , ans1);
return 0;
}