牛客 NC14414 小AA的数列(每日一题 5月25日 前缀异或和、位运算)

NC14414 小AA的数列

题目链接:https://ac.nowcoder.com/acm/problem/14414

题目描述

小AA找到了一个数列,她想要知道这个数列中所有长度为偶数的区间异或和之和 。
后来她发现这个问题太简单了,于是她加了一个限制,要求区间长度在[L,R]之间,
然后她就不会了。。。
请你告诉她问题的答案。

输入描述:

第一行三个数 n , L , R ( n 1 0 5 , 1 L R n ) n, L, R (n\le10^5,1\le L\le R\le n)
第二行 n n 个数表示这个数列。 ( a i 1 0 9 ) (a_i\le 10^9)

输出描述:

输出一行表示答案,由于答案可能很大,请输出答案模 1 0 9 + 7 10^9+7 的值。

示例1

输入
5 1 5
1 2 3 4 5
输出
16

示例2

输入
4 1 4
4 4 4 4
输出
0

这个题我理解起来超难!!!好长时间没做题了,理解起来太困难了。

code1

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e5 + 5;
const int MOD = 1e9 + 7;
int sum[N][30];    //前i个数 的第j位上 的前缀和
int cnt[N][30][2]; //前i个 第j位上的前缀和 中 前缀和是奇数和偶数的个数,0偶,1奇
int n, L, R;
int main()
{
    ios::sync_with_stdio(false), cin.tie(nullptr);
    cin >> n >> L >> R;
    for (int i = 1; i <= n; i++)
    {
        int t;
        cin >> t;
        for (int j = 0; j < 30; j++)
        {
            sum[i][j] = sum[i - 1][j] + ((t >> j) & 1); //对第i个数,求每一位的前缀和
            if (i != 1)
            {
                cnt[i][j][0] = cnt[i - 2][j][0]; //先继承前面的,再计算本位的
                cnt[i][j][1] = cnt[i - 2][j][1]; // i-2是因为长度为偶数的区间
            }
            cnt[i][j][sum[i][j] & 1]++; //判断本位上前缀和的奇偶
        }
    }
    int ans = 0;
    for (int i = 1; i <= n; i++) //枚举左端点
    {
        int l = i + L - 1, r = min(n, i + R - 1); //l是最短区间的右端点,r是最长区间的右端点
        if ((l - i + 1) & 1)                              //区间长度 l-i+1 要求是偶数
            l++;
        if ((r - i + 1) & 1)
            r--;
        if (l > r)
            continue;
        for (int j = 0; j < 30; j++)
        {
            int k = sum[i - 1][j] & 1; //前i-1个数的第j位的前缀和的奇偶,下面计算时要奇偶相反
            ans = (ans + (1LL << j) * 1LL * (cnt[r][j][k ^ 1] - cnt[l - 2][j][k ^ 1])) % MOD;
        }
    }
    cout << ans << endl;
    return 0;
}

code2

在这里插入图片描述

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e5 + 5;
const ll mod = 1e9 + 7;
int a[N];
int cnt[2][2];
int main()
{
    ios::sync_with_stdio(false), cin.tie(nullptr);
    int n, l, r;
    cin >> n >> l >> r;
    for (int i = 1; i <= n; i++)
        cin >> a[i], a[i] ^= a[i - 1]; //可以得到每一位的异或和(0或1)
    int ans = 0;
    for (int i = 0; i < 30; i++) //1e9 30位足够
    {
        memset(cnt, 0, sizeof(cnt));
        for (int j = l; j <= n; j++) //对 n 个数,从左往右,枚举右端点,左端点就是 (j-l+1) 和 (j-r+1),设左端点 k 在 [j-r+1,j-l+1] 之间,需要求位置 j 和位置 k-1 处的异或和奇偶性不同的位置的个数
        {
            cnt[(j - l) & 1][(a[j - l] >> i) & 1]++; //记下左端点 (j-l+1) 左边一位的奇偶性
            if (j > r)
                cnt[(j - r - 1) & 1][(a[j - r - 1] >> i) & 1]--;                //如果左端点 (j-r+1) 落在了 n 个数中,把左端点 (j-r+1) 的左边的所有位置的奇偶性删除
            ans = (ans + cnt[j & 1][((a[j] >> i) & 1) ^ 1] * (1LL << i)) % mod; //最后 cnt[n个数的奇偶位置][前缀和的奇偶性] 记录的就是区间 [j-r,j-l] 中所有数的前缀异或和的奇数或偶数的个数
        }
    }
    cout << ans << endl;
    return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_44169557/article/details/107507418