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\) 连一条无向边。 \(\oplus\) 代表异或

如果两个点互相连边只计算一条。

问你最少去掉几个点之后能让剩下的图为一颗树。

思路

好巧妙的题...

  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;
}
posted @ 2020-11-21 18:00  ullio  阅读(87)  评论(0编辑  收藏  举报