Codeforces Round #765 (Div. 2)D题

Codeforces Round #765 (Div. 2)D题

题意

给定一个集合\(S=\{a_1,a_2,\dots,a_n\}\)。给定一个数\(k\),找出\(S\)的一个最大的子集\(T\),满足对于任意的\(a,b\in T\),都有\(a\oplus b\geq k\)

数据范围满足

\(2\leq n\leq 3\times 10^5\)

\(0\leq a_i, k\leq2^{30}-1\)

若不存在满足题意的子集\(T\),输出\(-1\)

若存在,输出两行,第一行包含一个整数\(l\),表示子集\(T\)的大小,第二行包含\(l\)个整数,表示该子集中的每个元素在集合\(S\)中的下标。

分析

如果没有任何的特殊性质,这就是一个一般图的最大团问题,是个NP-Hard问题。所以这道题只能去挖掘性质。

如果存在\(3\)个互不相同的数\(a,b,c\),不妨设\(a<b<c\),尝试探寻一下\(a\oplus b, b\oplus c, a\oplus c\)之间的大小关系。

由于\(a<b<c\),故其二进制表示只能是如下两种形式(x表示该位相同,?表示该位任意)

  a:xxxx...x0???...?????...
  b:xxxx...x1xxx...x0???...
  c:xxxx...x1xxx...x1???...
a^b:0000...01???...?????...
b^c:0000...00000...01???...
a^c:0000...01???...?????...

或者

  a:xxxx...x0xxx...x0???...
  b:xxxx...x0xxx...x1???...
  c:xxxx...x1???...?????...
a^b:0000...00000...01???...
b^c:0000...01???...?????...
a^c:0000...01???...?????...

发现不论是上面的哪种情况都有\(a\oplus c \geq \min(a\oplus b, b\oplus c)\)

故只要满足\(a\leq b\leq c\)(加上等号,显然\(a\oplus c \geq \min(a\oplus b, b\oplus c)\)也成立),就可以由\(a\oplus b\geq k\)\(b\oplus c \geq k\),推出\(a\oplus c \geq k\)

所以,我们可以先给集合\(S\)升序排序,令\(f_i\)为满足题意的,最大值小于等于\(a_i\)的最大子集的大小,利用上面的性质,可以很容易写出转移方程,时间复杂度为\(O(n^2)\)

然而对于\(n\)的范围来讲,这个复杂度还是太高了,考虑优化。由于问题与异或相关,所以可以考虑字典树,把状态定义在字典树上,令\(f_i\)为满足题意的,最大值的前缀为字典树上\(i\)号结点所表示的前缀的最大子集的大小。从小到大依次插入集合\(S\)中的元素,每次在插入时更新对应的链上的\(f\),这条链上的\(f\)的更新源自满足与当前插入元素异或大于等于\(k\)的前缀的对应结点,故每次更新只需要\(O(\log a_{max})\),所以总体时间复杂度为\(O(n\log a_{\max})\)

代码

#include <algorithm>
#include <cstdio>
#include <utility>
using namespace std;
typedef pair<int, int> Pii;
const int maxn = 3e5 + 10;
const int maxm = 4e6 + 10;
const int dep = 30;
Pii dp[maxm], a[maxn];
int n, k, fa[maxn], tot, ch[maxm][2];
Pii query(int t) {
    Pii res = {0, 0};
    int u = 0;
    // t^?>=k
    // 在某一位上
    // t=0,k=0: 1 可行; 0 继续;
    // t=0,k=1: 1 继续; 0 不行;
    // t=1,k=0: 1 继续; 0 可行;
    // t=1,k=1: 1 不行; 0 继续;
    // 总结:每次只需向t^k儿子继续
   	// 若k=0,查看有无!(t^k)儿子,若有更新res
    for (int i = dep - 1; i >= 0; i--) {
        int v = ((t ^ k) >> i) & 1;
        if (!((k >> i) & 1) && ch[u][!v]) {
            res = max(res, dp[ch[u][!v]]);
        }
        if (!ch[u][v])
            return res;
        u = ch[u][v];
    }
    // 如果一直继续到底,说明t^?=k,?满足条件,更新res
    res = max(res, dp[u]);
    return res;
}
void insert(int t, Pii p) {
    int u = 0;
    for (int i = dep - 1; i >= 0; i--) {
        int v = (t >> i) & 1;
        if (!ch[u][v])
            ch[u][v] = ++tot;
        dp[u] = max(dp[u], p);
        u = ch[u][v];
    }
    dp[u] = max(dp[u], p);
}
int main() {
    scanf("%d%d", &n, &k);
    for (int i = 1; i <= n; i++) {
        scanf("%d", &a[i].first);
        a[i].second = i;
    }
    sort(a + 1, a + 1 + n);
    for (int i = 1; i <= n; i++) {
        Pii res = query(a[i].first);
        fa[a[i].second] = res.second;
        res.second = a[i].second;
        res.first++;
        insert(a[i].first, res);
    }
    if (dp[0].first >= 2) {
        printf("%d\n", dp[0].first);
        for (int i = dp[0].second; i; i = fa[i]) {
            printf("%d ", i);
        }
        puts("");
    } else {
        puts("-1");
    }
    return 0;
}
posted @ 2022-01-13 22:31  聆竹听风  阅读(58)  评论(0)    收藏  举报