【题解】异或序列

题目描述

Venn有一个数列 a 1 , a 2 , . . . , a n a_1,a_2,...,a_n a1,a2,...,an.有一天,BLUESKY007拿来了一个正整数 X X X。Venn是一个特别喜欢异或(xor)运算的孩子,她也很喜欢BLUESKY007。于是,Venn就想知道,自己能找到多少对数(i,j)能够满足 a i   x o r   a j = X a_i \ xor \ a_j = X ai xor aj=X. 两个数对 ( i 1 , j 1 ) (i_1,j_1) (i1,j1) ( i 2 , j 2 ) (i_2,j_2) (i2,j2)不同,当且仅当 i 1 ≠ i 2 i_1 \neq i_2 i1=i2或者 j 1 ≠ j 2 j_1 \neq j_2 j1=j2

思路

这题暴力肯定要超时(50分)。固要用二分查找。

解析

这道题除了用上面的算法外,还有一个比较容易理解的方法,详见代码。

这道题首先需要知道一个性质:
如果 a ^ b = c, 则 a ^ c = b, b ^ c = a.

当你有一个有序数组, 应该怎样快速的判断这个数组里有多少个值等于 x 的数呢?

eg. a[] = {1, 2, 4, 5, 5, 6, 6, 10, 23, 74}; 且数组长度 n = 10, 你要查询这个数组里出现了多少次 x = 5.

先求出第一个大于 x 的数的地址, 再求出第一个大于等于 x 的数的地址, 两者相减就是答案. 即:

ans = std::upper_bound(a, a+n, x) - std::lower_bound(a, a+n, x);

那么有同学要问了: std::upper_boundstd::lower_bound 不是返回地址吗?

Answer:

 (std::upper_bound(a, a+n, x) - a) - (std::lower_bound(a, a+n, x) - a)
=std::upper_bound(a, a+n, x) - a - std::lower_bound(a, a+n, x) + a // 两个 a 抵消, 得
=std::upper_bound(a, a+n, x) - std::lower_bound(a, a+n, x)

时间复杂度为 O ( l o g n ) ≪ O ( n ) O(log n) \ll O(n) O(logn)O(n).

这道题还有一个比较坑的, 就是时限太小. 所以你必须手写排序或者手写二分以提高效率.

#include <iostream>
#include <algorithm>
#include <cstring>
typedef long long LL;
const int N = 1e6 + 9;
int n, x, a[N];
int b[N], c[260];
void rsort() {
    for (int k = 0; k < 32; k += 8) {
        memset(c, 0, sizeof c);
        for (int i = 1; i <= n; ++i) ++c[a[i]>>k & 0xFF];
        for (int i = 1; i <= 0xFF; ++i) c[i] += c[i-1];
        for (int i = n; i; --i) {
            int t = a[i]>>k & 0xFF;
            b[c[t]--] = a[i];
        }
        memcpy(a+1, b+1, n*sizeof(int));
    }
}
int main() {
    scanf("%d%d", &n, &x);
    for (int i = 1; i <= n; i++)
        scanf("%d", a + i);
    rsort();
    LL ans = 0, last = 0;
    for (int i = 1; i <= n; i++) {
        if (a[i] == a[i-1]) {
            ans += last;
            continue;
        }
        int t = a[i] ^ x;
        last = std::upper_bound(a+1, a+n+1, t) - std::lower_bound(a+1, a+n+1, t);
        ans += last;
        // printf("when i=%d, last=%d\n", i, last);
    }
    printf("%lld", ans);
    return 0;
}
  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值