NC14414 小AA的数列
题目链接:https://ac.nowcoder.com/acm/problem/14414
题目描述
小AA找到了一个数列,她想要知道这个数列中所有长度为偶数的区间异或和之和 。
后来她发现这个问题太简单了,于是她加了一个限制,要求区间长度在[L,R]之间,
然后她就不会了。。。
请你告诉她问题的答案。
输入描述:
第一行三个数
第二行
个数表示这个数列。
输出描述:
输出一行表示答案,由于答案可能很大,请输出答案模 的值。
示例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;
}