Codeforces Round #765 (Div. 2)D题
Codeforces Round #765 (Div. 2)D题
题意
给定一个集合。给定一个数,找出的一个最大的子集,满足对于任意的,都有。
数据范围满足
若不存在满足题意的子集,输出。
若存在,输出两行,第一行包含一个整数,表示子集的大小,第二行包含个整数,表示该子集中的每个元素在集合中的下标。
分析
如果没有任何的特殊性质,这就是一个一般图的最大团问题,是个NP-Hard问题。所以这道题只能去挖掘性质。
如果存在个互不相同的数,不妨设,尝试探寻一下之间的大小关系。
由于,故其二进制表示只能是如下两种形式(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???...?????...
发现不论是上面的哪种情况都有
故只要满足(加上等号,显然也成立),就可以由且,推出。
所以,我们可以先给集合升序排序,令为满足题意的,最大值小于等于的最大子集的大小,利用上面的性质,可以很容易写出转移方程,时间复杂度为。
然而对于的范围来讲,这个复杂度还是太高了,考虑优化。由于问题与异或相关,所以可以考虑字典树,把状态定义在字典树上,令为满足题意的,最大值的前缀为字典树上号结点所表示的前缀的最大子集的大小。从小到大依次插入集合中的元素,每次在插入时更新对应的链上的,这条链上的的更新源自满足与当前插入元素异或大于等于的前缀的对应结点,故每次更新只需要,所以总体时间复杂度为
代码
#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 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步