Rinne Loves Xor

Rinne Loves Xor

https://ac.nowcoder.com/acm/contest/5505/B

链接:https://ac.nowcoder.com/acm/contest/5505/B
来源:牛客网

时间限制:C/C++ 2秒,其他语言4秒
空间限制:C/C++ 262144K,其他语言524288K
64bit IO Format: %lld

题目描述
在这里插入图片描述

输入描述:
第一行一个整数 N,表示数组 A 和 B 的长度。
第二行 N 个整数表示数组 A。
第三行 N 个整数表示数组 B。
输出描述:
输出一行 N 个整数,表示加密后的数组 C。
示例1
输入
复制

10
65605 70259 77306 43823 61443 98602 9261 7662 46394 83019
81393 5966 61479 24259 92528 96132 35859 47981 11702 71736

输出
复制

15796 166270 623824 1132402 1650729 2445262 3256941 4150718 5106184 6353038

备注:
N≤10^5^, ai≤10^9^

题解:

本人也没做出来,看了其他题解,学到两个方法

方法一

参考题解
一下为个人结合题解的理解
暴力做O(n^2^)肯定超时
在这里插入图片描述
题目已经发式子给我们了,我们可以看出整个求解其实就是一个递推过程。由已知推位置,之前推出的后面也不会再修改。
异或:相同为0,不同为非0
最麻烦的就是后面这个部分在这里插入图片描述
这个累加式子我们可以拆开
在这里插入图片描述
既然是异或,我们就用二进制来考虑,a与b异或,我们也可以把a与b都分成二进制,题目给的a的范围小于1e9,也就是a二进制最多为32位,所以这样完全ok
两个数位不同为1,如果当前数字二进制是1,前面这个数的这个位置的数如果是0,就可以贡献出1;反之也是。如果两个相同,则无法贡献
我们用到一个sum数组
sum[i][0]

sum1[i][0] 表示前面的 a 数组中二进制第 i 位为0 的数目
sum1[i][1]sum1[i][1] 表示前面的 a 数组中二进制第 i 位为 1 的数目
sum2[i][0]sum2[i][0] 表示前面的 b 数组中二进制第 i 位为 0 的数目
sum2[i][1]sum2[i][1] 表示前面的b 数组中二进制第 i 位为 1 的数目
代码为题解里的代码

#include<bits/stdc++.h>
using namespace std;

const long long inf = 1e18;
const int N = 1e6 + 5;
const double eps = 1e-10;
const int mod = 1e9 + 7;
typedef long long ll;

ll a[N], b[N];
ll ans[N];
ll sum1[64][2], sum2[64][2];
ll qpow(ll a, ll b) {
  ll res = 1;
  while(b) {
    if(b & 1) {
      res = res * a % mod;
    }
    a = a * a % mod;
    b >>= 1;
  }
  return res;
}
int main() {
  int n;
  cin >> n;
  for(int i = 1; i <= n; i++) cin >> a[i];
  for(int i = 1; i <= n; i++) cin >> b[i];
  for(int i = 1; i <= n; i++) {
    ll p = ans[i - 1] + (a[i] ^ b[i]) % mod;
    for(int j = 0; j <= 32; j++) {
      if(a[i] & (1LL << j)) { // 该位为1
        p += qpow(2, j) * sum2[j][0] % mod;
      }
      else { // 该位为0
        p += qpow(2, j) * sum2[j][1] % mod;
      }
      //cout << j << ' ' << sum2[j][0] << ' ' << sum2[j][1] << "\n";
    }
    for(int j = 0; j <= 32; j++) {
      if(b[i] & (1LL << j)) { // 该位为1
        p += qpow(2, j) * sum1[j][0] % mod;
      }
      else { // 该位为0
        p += qpow(2, j) * sum1[j][1] % mod;
      }
    }
    ans[i] = p % mod;
    for(int j = 0; j <= 32; j++) {
      if(a[i] & (1LL << j)) {
        sum1[j][1]++;
      }
      else sum1[j][0]++;
    }
    for(int j = 0; j <= 32; j++) {
      if(b[i] & (1LL << j)) {
        sum2[j][1]++;
      }
      else sum2[j][0]++;
    }
  }
  for(int i = 1; i <= n; i++) {
    cout << ans[i] % mod << " \n"[i == n];
  }
  return 0;
}

方法二:

个人感觉和上一个方法处理思想其实差不多
也是分成二进制进行对应数位异或
式子:
Ci=Ci-1+ai xor b1+ai xor b2+.....ai xor bi +ai-1 xor bi +...a1 xor bi

如果当前位数j,之前出现过4次1,另外一组出现过3次0,那么后面计算这一位给答案贡献就会是(4 * 3)<< j

pa[j][0/1]和pb[j][0/1]分别是a和b的在第j位之前0/1的数量
有个式子为
c[i] += (pa[j][0] * pb[j][1] + pa[j][1] * pb[j][0]) << j;
括号里就是相对应数位的数进行异或,而后面的<<j就是把这个数位的二进制转化成对应的十进制加给c

#include <iostream>
using namespace std;
typedef long long ll;

const int MOD = 1e9 + 7;
const int maxn = 1e5 + 2;
ll a[maxn], b[maxn], c[maxn];
ll pa[37][2], pb[37][2];

int main() {
    int n; 
    cin>>n; 
    for (int i = 1; i <= n; i++) cin>>a[i];
    for (int i = 1; i <= n; i++) cin>>b[i];
    for (int i = 1; i <= n; i++)
        for (int j = 0; j <= 30; j++) {
            pa[j][(a[i] >> j) & 1]++;
            pb[j][(b[i] >> j) & 1]++;//
            c[i] += (pa[j][0] * pb[j][1] + pa[j][1] * pb[j][0]) << j;
            c[i] %= MOD;
        }
    for (int i = 1; i <= n; i++)
        printf("%lld ", c[i]);
    return 0;
}
全部评论

相关推荐

到我怀里来:教育背景不用写主修课程,还有你写班级排名是什么意思?咋不写寝室排名呢😂要写就写年纪排名。得了那么多奖结果项目经历什么技术细节都不写清楚,把技术细节写清楚,用了什么技术解决了什么问题,“用了python语言、用了SQL语言”,有这样写的?hr一看就知道你是包装的或者比赛的奖是混的,你什么技术细节都不懂。校内职务全删了,什么三好学生、文明寝室这些你写上去干嘛?重复的奖学金你写三次干嘛?自我评价写那么多干嘛?谁想看这些
点赞 评论 收藏
分享
头发暂时很多的KFC总裁:找廉价劳动力罢了
点赞 评论 收藏
分享
评论
点赞
收藏
分享
牛客网
牛客企业服务