CF220B

Description

给定 (n) 个数组成的数列,(m) 次询问
对于每次询问求出在给定的 (L)(R) 的范围内有多少个数字的出现频率等于其本身

Solution

莫队
根据数据范围 (1le n,m le 1e5)(a_ile 1e9) 可知
当序列中某个数大于 (1e5) 时,他的出现频率不可能等于其大小,对答案无贡献
因此,对大于 (1e5) 的数,预处理为 (0)

然后
对于缩进操作,显然是删去当前点的贡献,再添加缩进之后的贡献,当删除的数等于其频率时,他的贡献会丢失,反之,如果删除之后才等于其频率,他的贡献可以增加
对于扩展操作原理亦是如此,只是先扩展再添加新贡献
另外要判断一下改变的数是否为预处理过的 (0)

Code

#include<bits/stdc++.h>
#define maxn 1001000
#define rr register 
//#define int long long

using namespace std;

int n,val[maxn],q,L,R=-1,sq,ans,siz[maxn],Res[maxn];
struct node{int l,r,id;}Ask[maxn]; 

inline int read() {
	int s=0,w=1;char ch=getchar();
	while(ch<'0'||ch>'9') {if(ch=='-') w=-1;ch=getchar();}
	while(ch>='0'&&ch<='9') s=(s<<1)+(s<<3)+(ch^48),ch=getchar();
	return w*s;
}

inline void write(int x) {
    if(x<0) putchar('-'),x=-x;
    if(x>9) write(x/10);
    putchar(x%10+'0');
}

void Del(int x){if(val[x]==0) return;ans-=(--siz[val[x]]==val[x]-1);ans+=(siz[val[x]]==val[x]);}
void Add(int x){if(val[x]==0) return;ans-=(++siz[val[x]]==val[x]+1);ans+=(siz[val[x]]==val[x]);}
inline int Cmp(node x,node y){return x.l/sq==y.l/sq?((x.l/sq)&1)?x.r<y.r:x.r>y.r:x.l/sq<y.l/sq;}

signed main(){
	n=read();q=read();sq=sqrt(n+0.5);
	for(rr int i=1;i<=n;i++){
		val[i]=read();
		if(val[i]>100000) val[i]=0;
	}
	for(rr int i=1;i<=q;i++) Ask[i].l=read(),Ask[i].r=read(),Ask[i].id=i;
	sort(Ask+1,Ask+1+q,Cmp);
	for(rr int i=1;i<=q;i++){
	    while(L>Ask[i].l){Add(--L);}
		while(R<Ask[i].r){Add(++R);}
		while(L<Ask[i].l){Del(L++);}
		while(R>Ask[i].r){Del(R--);}
		Res[Ask[i].id]=ans;
	}
	for(int i=1;i<=q;i++) printf("%d
",Res[i]);
	return 0;
}
原文地址:https://www.cnblogs.com/KnightL/p/14130605.html