Tokitsukaze and Min-Max XOR
Tokitsukaze and Min-Max XOR
题目描述
Tokitsukaze 有一个长度为 的序列 和一个整数 。
她想知道有多少种序列 ,满足:
其中 为按位异或,具体参见 百度百科:异或
答案可能很大,请输出 后的结果。
输入描述:
第一行包含一个整数 (),表示 组测试数据。
对于每组测试数据:
第一行包含两个整数 , (; )。
第二行包含 个整数 ()。
保证 不超过 。
输出描述:
对于每组测试数据,输出一个整数,表示答案 后的结果。
示例1
输入
3
3 2
1 3 2
5 3
1 3 5 2 4
5 0
0 0 0 0 0
输出
6
10
31
说明
第一组测试数据, 为 :
- 选择的序列 为 ,;
- 选择的序列 为 ,;
- 选择的序列 为 ,;
- 选择的序列 为 ,;
- 选择的序列 为 ,;
- 选择的序列 为 ,;
所以第一组测试数据的答案为 。
解题思路
看了点提示就做出来了,解法和昨天想到的思路差不多一样,不过最后没多少时间写了。
容易知道 实际上是 的一个子序列,并且由于我们只关注子序列中的最大值和最小值,因此可以先对 从小到大排序,再选择子序列。接着对子序列中的最大值进行分类,可以分成 类。即从左到右依次枚举 作为子序列中的最大值,那么最小值就会在 中选。当满足 ,那么以 为最大值, 为最小值的子序列的数量就是 ,特别的当 时答案为 。
暴力的做法就是逐个枚举 判断是否满足条件,时间复杂度是 的。由于涉及到异或运算所以尝试能不能用 trie 来维护 的信息。如果 满足条件,那么对答案的贡献是 ,也就是 ,因此在把 按位插入 trie 中时,同时在对应节点加上 。
枚举到 时,此时已经往 trie 中插入了 ,枚举 的每一位,用 和 分别表示 和 在二进制下第 位上的值。如果 ,说明此时 的分支剩余的 都满足条件,把该分支节点上的关于 的和累加到答案 。同理如果 ,说明此时 的分支剩余的 都满足条件。然后走到下一个分支节点,如果 则走到 的分支节点,否则走到 的分支节点。最后以 为最大值的子序列的数量就是 。
AC 代码如下,时间复杂度为:
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 2e5 + 10, mod = 1e9 + 7;
int n, m;
int a[N];
int tr[N * 30][2], idx, s[N * 30];
int qmi(int a, int k) {
int ret = 1;
while (k) {
if (k & 1) ret = 1ll * ret * a % mod;
a = 1ll * a * a % mod;
k >>= 1;
}
return ret;
}
void add(int x, int c) {
int p = 0;
for (int i = 29; i >= 0; i--) {
int t = x >> i & 1;
if (!tr[p][t]) tr[p][t] = ++idx;
p = tr[p][t];
s[p] = (s[p] + c) % mod;
}
}
int query(int x, int c) {
int p = 0, ret = 0;
for (int i = 29; i >= 0; i--) {
int t = x >> i & 1;
if (tr[p][0] && t < (m >> i & 1)) ret = (ret + 1ll * s[tr[p][0]] * c) % mod;
if (tr[p][1] && (t ^ 1) < (m >> i & 1)) ret = (ret + 1ll * s[tr[p][1]] * c) % mod;
if (t == (m >> i & 1)) {
if (!tr[p][0]) return ret;
else p = tr[p][0];
}
else {
if (!tr[p][1]) return ret;
else p = tr[p][1];
}
}
ret = (ret + 1ll * s[p] * c) % mod;
return ret;
}
void solve(){
scanf("%d %d", &n, &m);
for (int i = 0; i < n; i++) {
scanf("%d", a + i);
}
sort(a, a + n);
idx = 0;
for (int i = 0; i <= n * 30; i++) {
tr[i][0] = tr[i][1] = s[i] = 0;
}
int ret = 0;
for (int i = 0; i < n; i++) {
ret = (ret + 1 + query(a[i], qmi(2, i))) % mod;
add(a[i], qmi(qmi(2, i + 1), mod - 2));
}
printf("%d\n", ret);
}
int main() {
int t;
scanf("%d", &t);
while (t--) {
solve();
}
return 0;
}
参考资料
【题解】2024牛客寒假算法基础集训营2:https://ac.nowcoder.com/discuss/1251379/
本文来自博客园,作者:onlyblues,转载请注明原文链接:https://www.cnblogs.com/onlyblues/p/18010174
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效
2022-02-06 金发姑娘和N头牛