莫队模板题:
题意:给你编号分别为1~n的人,第i个人有一个颜色colori,每次询问求出[l, r]这个区间内最多可以组成多少对颜色相同的人
在该区间内,成对条件就是cnt[x]%2==0也就是这个数x出现的次数是偶数,否则就不成对ans--。
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e6;
struct node{ //离线记录查询操作
int L, R, k; //k:查询操作的原始顺序
}q[maxn];
int pos[maxn];
int ans[maxn];
int cnt[maxn]; //cnt[i]: 统计数字i出现了多少次
int a[maxn];
bool cmp(node a, node b){
//按块排序,就是莫队算法:
if(pos[a.L] != pos[b.L]) //按L所在的块排序,如果块相等,再按R排序
return pos[a.L] < pos[b.L];
if(pos[a.L] & 1) return a.R > b.R; //奇偶性优化,如果删除这一句,性能差一点
return a.R < b.R;
/*如果不按块排序,而是直接L、R排序,就是普通暴力法:
if(a.L==b.L) return a.R < b.R;
return a.L < b.L; */
}
int ANS = 0;
void add(int x){ //扩大区间时(L左移或R右移),增加数x出现的次数
cnt[a[x]]++;
if(cnt[a[x]]%2==0) ANS++; //这个元素第1次出现
}
void del(int x){ //缩小区间时(L右移或R左移),减少数x出现的次数
cnt[a[x]]--;
if(cnt[a[x]]&1) ANS--; //这个元素消失了
}
int main(){
int n; scanf("%d",&n);
int block = sqrt(n); //每块的大小
for(int i=1;i<=n;i++){
scanf("%d",&a[i]); //读第i个元素
pos[i]=(i-1)/block + 1; //第i个元素所在的块
}
int m; scanf("%d",&m);
for(int i=1;i<=m;i++){ //读取所有m个查询,离线处理
scanf("%d%d",&q[i].L, &q[i].R);
q[i].k = i; //记录查询的原始顺序
}
sort(q+1, q+1+m, cmp); //对所有查询排序
int L=1, R=0; //左右指针的初始值。思考为什么?
for(int i=1;i<=m;i++){
while(L<q[i].L) del(L++); //{del(L); L++;} //缩小区间:L右移
while(R>q[i].R) del(R--); //{del(R); R--;} //缩小区间:R左移
while(L>q[i].L) add(--L); //{L--; add(L);} //扩大区间:L左移
while(R<q[i].R) add(++R); //{R++; add(R);} //扩大区间:R右移
ans[q[i].k] = ANS;
}
for(int i=1;i<=m;i++) printf("%d\n",ans[i]); //按原顺序打印结果
return 0;
}