1. 程式人生 > 實用技巧 >Codeforces Round #683 (Div. 2, by Meet IT) E

Codeforces Round #683 (Div. 2, by Meet IT) E

Codeforces Round #683 (Div. 2, by Meet IT) E

大意

給你 \(n\) 個各不相同的數,寫在 \(n\) 個點上。

記寫在第 \(i\) 個點上的數為 \(a_i\) ,則對於任意點 \(i\) 會與使 \(a_i\oplus a_j\) 最小化的點 \(j\) 連一條無向邊。

如果兩個點互相連邊只計算一條。

問你最少去掉幾個點之後能讓剩下的圖為一顆樹。

思路

好巧妙的題...

  1. 原圖一定是樹或森林,不可能出現環。

    證:

    不失一般性,如果存在環,我們將環取出,重新標號為 \(1,...,k\) ,規定 \(a\) 連向 \(a+1\)\(k\)

    連向 \(1\)

    考慮 \(1\rightarrow2\) 的邊,按照規定,因為有 \(2\rightarrow 3\) ,所以 $ a_2\oplus a_3 < a_1\oplus a_2$ 。

    考慮 \(3\rightarrow 4 \ ...\) 顯然最後有 \(a_k\oplus a_1 < a_{k-1}\oplus a_k < ... <a_1\oplus a_2\)\(a_1\oplus a_2 < a_1\oplus a_k\) ,所以不難發現這個環是非法的。

    也就是說按照題述規則連結不會出現環。

  2. \(a_i\) 按照二進位制下最大的 \(1\)

    的位置分為兩個集合 \(S_0, S_1\)

    \(\exists k\ ; s_i\in S_1,s_j\in S_0\ ;s_j<2^k\leq s_i<2^{k+1}\)

    可以發現此時若 \(|S_0|>1\ and\ |S_1|>1\) 那麼原圖一定不是一棵樹。

    因為兩個集合的點只會與和自己處於相同集合內的點連邊。

    因為最少刪除就是最多保留。

    所以我們最多隻能讓其中一個集合保留一個點,另外一個保留儘量多的點。

    \(R(S_i)\) 為該集合最多保留的點, \(S_j,S_k\)\(S_i\) 在上述分法下的子集。

    按照上述分法,兩個子集的最多可以保留的點的數量是互不影響的,因為它們之間在元素數量大於一時不可能有邊。

    那麼當 \(R(S_j) \neq 0 \ and \ R(S_k) \neq 0\) ,能發現 \(R(S_i) = max(R(S_j),R(s_k))+1\)

    如果 \(R(S_j) = 0\) 那麼 \(R(S_i) = R(S_k)\)

    於是我們可以遞迴處理這個問題了。

程式碼

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
using namespace std;

#define ll long long
#define ull unsigned long long
#define cint const int&
#define Pi acos(-1)

const int mod = 998244353;
const int inf_int = 0x7fffffff;
const ll inf_ll = 0x7fffffffffffffff;
const double ept = 1e-9;

int n;
int a[200100];

int dfs(cint l, cint r, cint num, int key) {
    if(l > r) return 0;
    if(l == r) return 1;
    int k = lower_bound(a+1, a+1+n, key+(1<<num)) - a;
    int x = dfs(l, k-1, num-1, key);
    int y = dfs(k, r, num-1, key+(1<<num));
    if(!(x*y)) return max(x, y);
    return max(x,y) + 1;
}

int main() {
    ios::sync_with_stdio(false);
    cin >> n;
    for(int i=1; i<=n; i++) cin >> a[i];
    sort(a+1, a+1+n);
    int t=0;
    while((1<<t) <= a[n]) ++t;
    cout << n-dfs(1, n, t-1, 0);
    return 0;
}