题意:在一个序列中,从l到r的x数中出现的次数正好是x次的情况有多少。
思路:离线做,先处理要求的区间,按r递增排序,然后每次处理序列中的一个数的时候,找出+1所在的区间,并将之前的区间-1恢复,用差分的方式处理树状数组。
AC代码如下:
#include<cstdio>
#include<cstring>
#include<map>
#include<vector>
#include<algorithm>
using namespace std;
map<int,int> match;
int tree[100010],n,m,num,ans[100010],val[100010],f[100010],p[100010];
vector<int> vc[100010];
struct node
{
int l,r,pos;
}op[100010];
bool cmp(node a,node b)
{
return a.r<b.r;
}
int lowbit(int x)
{
return x&(-x);
}
void update(int x,int val)
{
for(;x<=n;x+=lowbit(x))
tree[x]+=val;
}
int query(int x)
{
int ret=0;
for(;x>0;x-=lowbit(x))
ret+=tree[x];
return ret;
}
int main()
{
int i,j,k,pos;
scanf("%d%d",&n,&m);
for(i=1;i<=n;i++)
{
scanf("%d",&val[i]);
f[i]=val[i];
}
sort(f+1,f+1+n);
for(i=1;i<=n;i++)
if(f[i]!=f[i-1])
{
num++;
match[f[i]]=num;
}
for(i=1;i<=m;i++)
{
scanf("%d%d",&op[i].l,&op[i].r);
op[i].pos=i;
}
for(i=1;i<=num;i++)
vc[i].push_back(0);
sort(op+1,op+1+m,cmp);
op[m+1].r=0;
pos=1;
for(i=1;i<=n;i++)
{
k=match[val[i]];
p[k]++;
vc[k].push_back(i);
if(p[k]>=val[i])
{
update(vc[k][p[k]-val[i]]+1,1);
update(vc[k][p[k]-val[i]+1]+1,-1);
if(p[k]>val[i])
{
update(vc[k][p[k]-val[i]-1]+1,-1);
update(vc[k][p[k]-val[i]]+1,1);
}
}
while(op[pos].r==i)
{
ans[op[pos].pos]=query(op[pos].l);
pos++;
}
}
for(i=1;i<=m;i++)
printf("%d\n",ans[i]);
}