Codeforces Round #765 (Div. 2)D题

Codeforces Round #765 (Div. 2)D题

题意

给定一个集合S={a1,a2,,an}。给定一个数k,找出S的一个最大的子集T,满足对于任意的a,bT,都有abk

数据范围满足

2n3×105

0ai,k2301

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

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

分析

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

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

由于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???...?????...

发现不论是上面的哪种情况都有acmin(ab,bc)

故只要满足abc(加上等号,显然acmin(ab,bc)也成立),就可以由abkbck,推出ack

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

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

代码

#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;
}

本文作者:聆竹听风

本文链接:https://www.cnblogs.com/Bamboo-Wind/p/15799990.html

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   聆竹听风  阅读(50)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起