AtCoder Beginner Contest 242——Range Pairing Query

莫队模板题:

题意:给你编号分别为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;
	}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值