【CF220B】Little Elephant and Array
题目
题目链接:https://codeforces.com/problemset/problem/220/B
小象喜欢和数组玩。现在有一个数组 \(a\),含有 \(n\) 个正整数,记第 \(i\) 个数为 \(A_i\)。
现在有 \(m\) 个询问,每个询问包含两个正整数 \(l_j\) 和 \(r_j(1\leq l_j \leq r_j \leq n)\),小象想知道在 \(A_{l_j}\) 到 \(A_{r_j}\) 之中有多少个数 \(x\),其出现次数也为 \(x\)。
思路
询问按 \(r\) 从小到大离线,然后双指针扫描询问和 \(A\)。
当扫到的数字是 \(x\) 时,如果前面已经有 \(k\) 个 \(x\) 出现(\(k\geq x-1\)),那么如果此时 \(r=x\),\(l\) 在第 \(x-k\) 和 \(x-k+1\) 个 \(x\) 之间是可以被 \(x\) 贡献的。同时需要减去上一次 \(x\) 的贡献。
问题转化为区间加单点求和,线段树即可。
时间复杂度 \(O(m\log n)\)。
代码
#include <bits/stdc++.h>
using namespace std;
const int N=100010;
int n,Q,a[N],ans[N];
vector<int> pos[N];
struct Query
{
int l,r,id;
}ask[N];
bool cmp(Query x,Query y)
{
return x.r<y.r;
}
int read()
{
int d=0; char ch=getchar();
while (!isdigit(ch)) ch=getchar();
while (isdigit(ch)) d=(d<<3)+(d<<1)+ch-48,ch=getchar();
return d;
}
struct SegTree
{
int cnt[N*4];
void pushdown(int x)
{
if (cnt[x])
{
cnt[x*2]+=cnt[x];
cnt[x*2+1]+=cnt[x];
cnt[x]=0;
}
}
void update(int x,int l,int r,int ql,int qr,int v)
{
if (l==ql && r==qr)
{
cnt[x]+=v;
return;
}
pushdown(x);
int mid=(l+r)>>1;
if (qr<=mid) update(x*2,l,mid,ql,qr,v);
else if (ql>mid) update(x*2+1,mid+1,r,ql,qr,v);
else update(x*2,l,mid,ql,mid,v),update(x*2+1,mid+1,r,mid+1,qr,v);
}
int query(int x,int l,int r,int k)
{
if (l==k && r==k) return cnt[x];
pushdown(x);
int mid=(l+r)>>1;
if (k<=mid) return query(x*2,l,mid,k);
else return query(x*2+1,mid+1,r,k);
}
}seg;
int main()
{
n=read(); Q=read();
for (int i=1;i<=n;i++)
{
a[i]=read();
pos[i].push_back(0);
}
for (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 (int i=1,j=1;i<=Q;i++)
{
for (;j<=ask[i].r;j++)
{
if (a[j]>n) continue;
pos[a[j]].push_back(j);
int k=pos[a[j]].size();
if (k>a[j])
{
if (k>a[j]+1)
seg.update(1,1,n+1,pos[a[j]][k-a[j]-2]+2,pos[a[j]][k-a[j]-1]+1,-1);
seg.update(1,1,n+1,pos[a[j]][k-a[j]-1]+2,pos[a[j]][k-a[j]]+1,1);
}
}
ans[ask[i].id]=seg.query(1,1,n+1,ask[i].l+1);
}
for (int i=1;i<=Q;i++)
printf("%d\n",ans[i]);
return 0;
}