CodeForces - 220B Little Elephant and Array(莫队数组优化)

题目链接https://vjudge.net/contest/355127#problem/H
在这里插入图片描述
翻译
给定n和m,n个数,m次询问。
每次给定一个区间[l,r],求区间内有几个数满足出现的次数等于数字本身
分析
每个数的范围为1<=a[i]<=1e9,对于一个数出现的次数,通常采用的方法为book[i]标记。
book的范围不可能开到1e9,所以要考虑对于一个1<=a[i]<=1e9的数,它的出现次数如何用book表示出来,这就需要缩小范围,或用特定的值代表这个数
解决
a[i]:表示输入的n个数
引入hash数组ha[i]=a[i]:第i个位置上的数为a[i],n的范围最大为1e5
所以,成功用最大范围1e5的ha数组,表示出来1e9的数
对于样例:

7 2
3 1 2 2 3 3 7

对ha数组从小到大排序后:

1 2 2 3 3 3 7

引入unique函数进行去重
unique:
不停的把后面的元素移到前面来,也可以说是用不重复的元素占领重复元素的位置
返回的是:去重后容器中不重复序列的最后一个元素的下一个元素的位置
如:

去重前:1 3 3 4 5 6 6 7
去重后:1 3 4 5 6 7 6 7

对ha数组进行unique处理:
length=unique(ha+1,ha+1+n)-(ha+1);

1 2 3 7 3 3 7

length=4
重新定义a数组

 for(int i=1; i<=n; i++)
        a[i]=lower_bound(ha+1,ha+1+length,a[i])-ha

在ha数组[1,length]的范围内,对于每一个a[i],a[i]此时等于第一个大于等于a[i]位置的下标。因为[1,length]内为不重复序列,所以a[i]一定等于,等于a[i]位置的下标
处理后a数组为:

3 1 2 2 3 3 4

求一个数i,出现的次数book[i]和这个数本身的表示,就可以表示为:
book[a[i]]:a[i]表示在ha数组中等于a[i]的下标,用下标来表示a[i]这个数本身
ha[a[i]]:a[i]表示在ha数组中等于a[i]的下标,所以ha[a[i]]就表示原来范围[1,1e9]的那个a[i]

接下来用莫队处理

完整代码

#include<cstdio>
#include<cstring>
#include<iostream>
#include<math.h>
#include<stdlib.h>
#include<algorithm>
using namespace std;
const int N=1e5+10;
int n,m;
int ha[N],a[N],book[N];
int sum;
struct node
{
    int l,r,id;
    int ans;
    int pos;
} val[N];
int cmp(node t1,node t2)/*莫队思想*/
{
    if(t1.pos==t2.pos)
        return t1.r<t2.r;
    return t1.pos<t2.pos;
}
int cmp1(node t1,node t2)
{
    return t1.id<t2.id;
}
void update(int o,int k)
{
    if(book[a[o]]!=ha[a[o]]&&book[a[o]]+k==ha[a[o]])
        sum++;
    else if(book[a[o]]==ha[a[o]]&&book[a[o]]+k!=ha[a[o]])
        sum--;
    book[a[o]]+=k;
}
int main()
{
    sum=0;
    scanf("%d%d",&n,&m);
    for(int i=1; i<=n; i++)
    {
        scanf("%d",&a[i]);
        ha[i]=a[i];/*第i个位置的数字为a[i]*/
    }
    sort(ha+1,ha+1+n);
    int length=unique(ha+1,ha+1+n)-(ha+1);
    for(int i=1; i<=n; i++)
        a[i]=lower_bound(ha+1,ha+1+length,a[i])-ha;/*下标*/
    int b=sqrt(n+0.0);
    for(int i=1; i<=m; i++)
    {
        scanf("%d%d",&val[i].l,&val[i].r);
        val[i].id=i;
        val[i].pos=val[i].l/b;
    }
    sort(val+1,val+1+m,cmp);
    int l=1,r=0;
    for(int i=1; i<=m; i++)
    {
        while(l<val[i].l)
            update(l++,-1);
        while(l>val[i].l)
            update(--l,1);
        while(r>val[i].r)
            update(r--,-1);
        while(r<val[i].r)
            update(++r,1);
        val[i].ans=sum;
    }
    sort(val+1,val+1+m,cmp1);
    for(int i=1; i<=m; i++)
        printf("%d\n",val[i].ans);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

zaiyang遇见

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值