CF #136Div2 D. Little Elephant and Array

                                               D. Little Elephant and Array


time limit per test4 seconds
memory limit per test256 megabytes
inputstandard input
outputstandard output
The Little Elephant loves playing with arrays. He has array a, consisting of n positive integers, indexed from 1 to n. Let's denote the number with index i as ai.

Additionally the Little Elephant has m queries to the array, each query is characterised by a pair of integers lj and rj (1 ≤ lj ≤ rj ≤ n). For each query lj, rj the Little Elephant has to count, how many numbers x exist, such that number x occurs exactly x times among numbers alj, alj + 1, ..., arj.

Help the Little Elephant to count the answers to all queries.

Input
The first line contains two space-separated integers n and m (1 ≤ n, m ≤ 105) — the size of array a and the number of queries to it. The next line contains n space-separated positive integers a1, a2, ..., an (1 ≤ ai ≤ 109). Next m lines contain descriptions of queries, one per line. The j-th of these lines contains the description of the j-th query as two space-separated integers lj and rj (1 ≤ lj ≤ rj ≤ n).

Output
In m lines print m integers — the answers to the queries. The j-th line should contain the answer to the j-th query.

Sample test(s)
input
7 2
3 1 2 2 3 3 7
1 7
3 4
output
3
1


题意:问在一段区间内有多少个数它本身的值和它出现的次数相同

思路:用线段树思想,成段更新,单点求值

由于是单点求和,所以采用松弛操作,向下更新子节点的值(lazy思想)-----因为往下才有l==r

对询问离线处理,对于每一个数x,当找到x个x时,那么以1到x第一次出现的位置i 即[1,i]这一段区间的人一多为起点,其所求个数都加1,当往右走,出现x+1个x时,前面[1,i]要更新减1,而在[i+1,j] 加1, j为第(x+1-x+1)个x的位置

看例子:

3 1 2 2 3 3 7 3

当找到1个1时即指针指向下标p=2,  [1,2]区间+1,2为1第一次出现的位置

当找到2个2时即指针指向下标p=4,  [1,3]区间+1

当找到3个3时即指针指向下标p=6,  [1,1]区间+1

当找到4个3时即指针指向下标p=8,  [1,1]区间-1, [2,5]区间+1,5为第(4-3+1)=2个3出现的位置

代码:

#include <iostream>//D
#include <stdio.h>
#include <cmath>
#include <cstring>
#include <algorithm>
#include <vector>
#include <queue>
using namespace std;
#define lson l,mid,2*num
#define rson mid+1,r,2*num+1
const int N=1e5+5;
struct Query
{
    int s,e;
    int pos;
} q[N];
bool cmp(Query a,Query b)
{
    return a.e < b.e;
}
vector<int>V[N];//记录每一个值x出现的位置
int n,m;
int a[N],cnt[N];//cnt[N]记录x出现的次数
int pre[N][2];//记录前一个已经更新了的区间
int ans[N];
int sum[4*N];
void relax(int num)//松弛操作
{
    int &s=sum[num];
    sum[num<<1]+=sum[num];
    sum[num<<1|1]+=sum[num];
    s=0;
}
void PushUp(int num)
{
    sum[num]=sum[num<<1]+sum[num<<1|1];
}
void build(int l,int r,int num)
{
    if(l==r)return;
    int mid=(l+r)/2;
    build(lson);
    build(rson);
}
void update(int L,int R,int l,int r,int num,int add)
{
    if(L<=l && R>=r)
    {
        sum[num]+=add;
        return;
    }
    relax(num);
    int mid=(l+r)/2;
    if(L<=mid)update(L,R,lson,add);
    if(R>mid)update(L,R,rson,add);
}
int query(int pos,int l,int r,int num)
{
    if(l==r)return sum[num];
    relax(num);
    int mid=(l+r)/2;
    if(pos<=mid)return query(pos,lson);
    else
    return query(pos,rson);
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1; i<=n; i++)
    {
        scanf("%d",&a[i]);
    }
    build(1,n,1);
    for(int i=1; i<=m; i++)
    {
        scanf("%d%d",&q[i].s,&q[i].e);
        q[i].pos=i;
    }
    sort(q+1,q+m+1,cmp);
    memset(cnt,0,sizeof(cnt));
    memset(sum,0,sizeof(sum));
    int num=1;
    for(int i=1; i<=n; i++)
    {
        int v=a[i];
        if(v<=n)
        {
            cnt[v]++;
            V[v].push_back(i);
            if(cnt[v]==v)
            {
                pre[v][0]=1;
                pre[v][1]=V[v][0];
                update(pre[v][0],pre[v][1],1,n,1,1);

            }
            else if(cnt[v]>v)
            {
                update(pre[v][0],pre[v][1],1,n,1,-1);
                pre[v][0]=pre[v][1]+1;
                pre[v][1]=V[v][cnt[v]-v];//求第cnt[v]-v+1个x出现的位置
                update(pre[v][0],pre[v][1],1,n,1,1);
            }
        }
        while(q[num].e==i && num <= m)
        {
            ans[q[num].pos]=query(q[num].s,1,n,1);
            num++;
        }
    }
    for(int i=1; i<=m; i++)
    {
        printf("%d\n",ans[i]);
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值