10770: 发财兔1C
时间限制: 1 Sec 内存限制: 128 MB
提交: 39 解决: 14
[提交] [状态] [讨论版] [命题人:admin]
题目描述
初始给定n个卡片拍成一排,其中第i个卡片上的数为x[i]。
有q个询问,每次询问给定L和R表示,询问的区间【L,R】内的卡片所有出现了偶数次的数的异或和是多少。
输入
输入一行两个整数n,q。
第二行n个整数,第i个数为x[i]。
接下来q行,每行两个整数L和R,表示询问的区间。
输出
输出q行,其中第i行表示第i次询问的区间出现偶数次的数的异或和。
样例输入
复制样例数据
3 1
3 7 8
1 3
样例输出
0
提示
对于20%的数据,n,q<=1000;
对于另外20%的数据,n,q<=50000,a[i]<=10。
对于100%的数据,n,q<=1000000,0<=a[i]<=10^9。
题意:求区间内出现次数为偶数的数的异或和
根据异或的特点,出现偶数次的数异或和为0,出现奇数次的数异或和就是去加内所有数的异或和
那么就转换为区间内出现奇数次的数的异或和 异或 该区间出现过的数的类型(即去重后)的异或和
出现奇数次的数的异或和使用前缀异或和数组即可
去重则使用树状数组,维护每个数只出现一次
对每个数,若已经出现过则删掉该位置,重新将现有的位置加入树状数组
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e6 + 100;
const int INF = 1e9;
const int MOD = 1e9 + 7;
ll a[maxn];
int last[maxn];
ll sum[maxn];
ll p[maxn];
ll ans[maxn];
map<ll, int> mapp;
struct node {
int l, r, id;
} s[maxn];
bool cmp(node a, node b) {
return a.r < b.r;
}
void update(int x, ll a) {
for (int i = x; i < maxn; i += i & (-i)) {
p[i] ^= a;
}
}
ll query(int x) {
ll res = 0;
for (int i = x; i > 0; i -= i & (-i)) {
res ^= p[i];
}
return res;
}
int main() {
int n, q;
scanf("%d%d", &n, &q);
for (int i = 1; i <= n; i++) {
scanf("%d", &a[i]);
last[i] = mapp[a[i]]; //记录该位置的数上一次出现的位置,若第一次出现则为0
mapp[a[i]] = i; //记录a[i]这个数最后一次出现的位置
sum[i] = sum[i - 1] ^ a[i]; //前缀异或和数组
}
for (int i = 1; i <= q; i++) {
scanf("%d%d", &s[i].l, &s[i].r);
s[i].id = i;
}
sort(s + 1, s + q + 1, cmp); //按照询问的右位置升序排列
int k = 1;
for (int i = 1; i <= n; i++) {
if (last[i]) update(last[i], a[i]); //删去上一次出现的数
update(i, a[i]); //增加现有数到树状数组
while (k <= q && i == s[k].r) {
ll v = query(s[k].l - 1);
ll u = query(s[k].r);
ans[s[k].id] = sum[s[k].l - 1] ^ sum[s[k].r] ^ v ^ u;
k++;
}
}
for (int i = 1; i <= q; i++) {
printf("%lld\n", ans[i]);
}
return 0;
}