Tokitsukaze and Min-Max XOR

Tokitsukaze and Min-Max XOR

题目描述

Tokitsukaze 有一个长度为 n 的序列 a1,a2,,an​ 和一个整数 k

她想知道有多少种序列 b1,b2,,bm​,满足:

  • 1bin
  • bi1<bi (2im)
  • min(ab1,ab2,,abm)max(ab1,ab2,,abm)k

其中 为按位异或,具体参见 百度百科:异或

答案可能很大,请输出  mod109+7 后的结果。

输入描述:

第一行包含一个整数 T (1T2105),表示 T 组测试数据。

对于每组测试数据:

第一行包含两个整数 n, k (1n2105; 0k109)。

第二行包含 n 个整数 a1,a2,,an​ (0ai109)。

保证 n 不超过 2105

输出描述:

对于每组测试数据,输出一个整数,表示答案 mod109+7 后的结果。

示例1

输入

3
3 2
1 3 2
5 3
1 3 5 2 4
5 0
0 0 0 0 0

输出

6
10
31

说明

第一组测试数据,k2

  1. 选择的序列 b[1]min(a1)max(a1)=11=02
  2. 选择的序列 b[2]min(a2)max(a2)=33=02
  3. 选择的序列 b[3]min(a3)max(a3)=22=02
  4. 选择的序列 b[1,2]min(a1,a2)max(a1,a2)=13=22
  5. 选择的序列 b[2,3]min(a2,a3)max(a2,a3)=23=12;
  6. 选择的序列 b[1,2,3]min(a1,a2,a3)max(a1,a2,a3)=13=22

所以第一组测试数据的答案为 6

 

解题思路

  看了点提示就做出来了,解法和昨天想到的思路差不多一样,不过最后没多少时间写了。

  容易知道 b1,,bm 实际上是 a 的一个子序列,并且由于我们只关注子序列中的最大值和最小值,因此可以先对 a 从小到大排序,再选择子序列。接着对子序列中的最大值进行分类,可以分成 n 类。即从左到右依次枚举 ai 作为子序列中的最大值,那么最小值就会在 aj,j[0,i] 中选。当满足 aiajk,那么以 ai 为最大值,aj 为最小值的子序列的数量就是 2max{0,ij1},特别的当 i=j 时答案为 1

  暴力的做法就是逐个枚举 aj 判断是否满足条件,时间复杂度是 O(n2) 的。由于涉及到异或运算所以尝试能不能用 trie 来维护 aj 的信息。如果 aj 满足条件,那么对答案的贡献是 2ij1,也就是 12j+12i,因此在把 aj 按位插入 trie 中时,同时在对应节点加上 12j+1

  枚举到 ai 时,此时已经往 trie 中插入了 a0ai1,枚举 ai 的每一位,用 ximi 分别表示 aim 在二进制下第 i 位上的值。如果 xi0<mi,说明此时 0 的分支剩余的 aj 都满足条件,把该分支节点上的关于 12j+1 的和累加到答案 s。同理如果 xi1<mi,说明此时 1 的分支剩余的 aj 都满足条件。然后走到下一个分支节点,如果 xi0=mi 则走到 0 的分支节点,否则走到 1 的分支节点。最后以 ai 为最大值的子序列的数量就是 1+s2i

  AC 代码如下,时间复杂度为O(n(logA+logn))

#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/

posted @   onlyblues  阅读(41)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效
历史上的今天:
2022-02-06 金发姑娘和N头牛
Web Analytics
点击右上角即可分享
微信分享提示