M-Upanishad
给出n个元素的数组a和m个区间询问。每次询问你区间内出现偶数次的所有元素的异或和。
首先可以想到,如果是条件是奇数显然要好处理不少的(直接区间xor),偶数实在不知道怎么处理。暴力 暴力是不可能暴力的,优雅一点,写莫队吧。很好,TLE了。
实际上偶数次出现可以转化为(区间内不同元素的异或)xor(区间异或)。那么区间内不同元素的异或怎么搞呢?上莫队!! 我们可以将询问离线并按照r排序,每次扩展右边界至当前询问,维护每一个元素离当前r最近出现的位置,并将这个位置之前出现的位置全部置0。这样我们询问的区间内每个元素就都只出现一次了。显然,这是可以用树状数组维护的。
看数据,记得离散化。完结撒花。
PS:看题解后居然贴心告诉我出题人卡莫队·····
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<map>
#include<queue>
#include<vector>
#include<set>
#include<list>
#include<bitset>
/*#include<bits/stdc++.h>*/
//#pragma GCC optimize(2)
using namespace std;
typedef long long LL;
const int maxn=2e6+22;
const LL INF=1e9+7;
const LL mod=1000000007;
void outLL(LL x)
{
if(x<0){x=~x+1;putchar('-');}
if(x>9)outLL(x/10);
putchar(char(x%10+'0'));
}
const double PI=acos(-1);
#define pii pair<int,int>
#define mp(x,y) make_pair(x,y)
#define sfi(a) scanf("%d",&a)
#define sfl(a) scanf("%lld",&a)
#define sff(a) scanf("%lf",&a)
int n,m;
int a[maxn],sxor[maxn],b[maxn];
struct zj
{
int l,r,id;
bool operator<(const zj b)const
{
return r<b.r;
}
}que[maxn];
int tre[maxn];
int ans[maxn];
int pre[maxn];
void addtre(int x,int val)
{
for(;x<=n;x+=(x&(-x)))
{
tre[x]^=val;
}
}
int querytre(int x)
{
int kk=0;
for(;x;x-=(x&(-x)))
{
kk^=tre[x];
}
return kk;
}
void add(int x)
{
if(pre[a[x]]!=0)
{
addtre(pre[a[x]],b[a[x]]);
}
addtre(x,b[a[x]]);
pre[a[x]]=x;
}
int main()
{
sfi(n);sfi(m);
for(int i=1;i<=n;++i)
{
sfi(a[i]);sxor[i]=sxor[i-1]^a[i];
b[i]=a[i];
}
sort(b+1,b+1+n);
for(int i=1;i<=n;++i)a[i]=lower_bound(b+1,b+1+n,a[i])-b;
for(int i=1;i<=m;++i)
{
sfi(que[i].l);sfi(que[i].r);que[i].id=i;
}
sort(que+1,que+1+m);
int l,r;
l=r=0;
for(int i=1;i<=m;++i)
{
int asd=sxor[que[i].r]^sxor[que[i].l-1];
while(r<que[i].r)add(++r);
l=que[i].l;
ans[que[i].id]=asd^querytre(r)^querytre(l-1);
}
for(int i=1;i<=m;++i)printf("%d\n",ans[i]);
}