[NOIP2017模拟]count

题目背景
SOURCE:NOIP2015-SHY-8

题目描述
给定一个元素个数为 n 的整数数组 a 和 Q 个问题,每个问题有 x,y 两个参数,请统计共有多少个整数 K 满足 K 在 a[x]…a[y] 中出现了恰好 K 次。

输入格式
第一行两个整数 n,Q,表示数组 a 的元素个数和询问数;
接下来一行 n 给整数,描述数组 a ;
接下来 Q 行,每行两个数 xi , yi (1≤ xi yi ≤n),表示询问的左右边界。

输出格式
输出 Q 行,每行一个整数表示满足询问的 K 的个数。

样例数据
输入
7 2
3 1 2 2 3 3 7
1 7
3 4
输出
3
1

备注
【样例说明】
Q1: 1、2、3 分别满足,所以共有 3 个数满足要求。
Q2: 2 满足,所以只有 1 个数满足要求。

【数据范围】
对 50% 的输入数据:1≤n,Q≤1000
对 100% 的输入数据:1≤n,Q≤100000,1≤a[i]≤ 109

分析:这道题用莫队算法,什么是莫队算法呢,就是用前一个询问更新后一个,而这道题要分块,好处是把排序O( n2 )降到了O(n n)。将询问排序,一个询问计算出来之后与下一个询问左右边界比较,然后移动左右边界更新答案。
注意:若前一个询问l(或r)在后一个之外,那么就要从l开始更新;而如果在后一个之内,就要从l之前一个(r之后一个)更新。

代码

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<ctime>
#include<cmath>
#include<algorithm>
#include<cctype>
#include<iomanip>
#include<queue>
#include<set>
using namespace std;

int getint()
{
    int sum=0,f=1;
    char ch;
    for(ch=getchar();(ch<'0'||ch>'9')&&ch!='-';ch=getchar());
    if(ch=='-')
    {
        f=-1;
        ch=getchar();
    }
    for(;ch>='0'&&ch<='9';ch=getchar())
        sum=(sum<<3)+(sum<<1)+ch-48;
    return sum*f;
}

const int maxn=100010;
struct node{
    int l,r,id,pos;
}q[maxn];
int n,Q,s,res;
int a[maxn],c[maxn],ans[maxn];

bool comp(const node &a,const node &b)
{
    if(a.id==b.id)
        return a.r<b.r;
    return a.id<b.id; 
}

int main()
{
    freopen("count.in","r",stdin);
    freopen("count.out","w",stdout);

    n=getint();Q=getint();
    for(int i=1;i<=n;++i)
    {
        a[i]=getint();
        if(a[i]<=n) c[a[i]]++;
    }

    for(int i=1;i<=n;++i)
        if(c[i]==i) res++;

    s=(int)sqrt(n);
    for(int i=1;i<=Q;++i)
    {
        q[i].l=getint(),q[i].r=getint();
        q[i].id=q[i].l/s+1;
        q[i].pos=i;
    }

    sort(q+1,q+Q+1,comp);

    int l=1,r=n;
    for(int i=1;i<=Q;++i)
    {
        while(l<q[i].l)
        {
            if(a[l]>n)
            {
                l++;
                continue;
            }
            if(a[l]==c[a[l]]) res--;
            if(a[l]==c[a[l]]-1) res++;
            c[a[l]]--;
            l++;
        } 

        while(l>q[i].l)//边界
        {
            l--;
            if(a[l]>n)
                continue;
            if(a[l]==c[a[l]]) res--;
            if(a[l]==c[a[l]]+1) res++;
            c[a[l]]++;
        }

        while(r>q[i].r)
        {
            if(a[r]>n)
            {
                r--;
                continue;
            }
            if(a[r]==c[a[r]]) res--;
            if(a[r]==c[a[r]]-1) res++;
            c[a[r]]--;
            r--;
        }

        while(r<q[i].r)//边界
        {
            r++;
            if(a[r]>n)
                continue;
            if(a[r]==c[a[r]]) res--;
            if(a[r]==c[a[r]]+1) res++;
            c[a[r]]++;
        }

        ans[q[i].pos]=res;
        l=q[i].l,r=q[i].r;
    }

    for(int i=1;i<=Q;++i)
        cout<<ans[i]<<'\n';
    return 0;
}

本题结。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值