bzoj3236 [Ahoi2013]作业

本文介绍如何使用莫队算法解决大规模数据区间统计问题,包括统计指定区间内符合特定条件的元素数量及其种类数量。文章提供了一个具体的编程实现案例,并通过样例展示了算法的应用效果。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

http://www.elijahqi.win/archives/3158
题目描述

此时己是凌晨两点,刚刚做了Codeforces的小A掏出了英语试卷。英语作业其实不算多,一个小时刚好可以做完。然后是一个小时可以做完的数学作业,接下来是分别都是一个小时可以做完的化学,物理,语文……小A压力巨大。

这是小A碰见了一道非常恶心的数学题,给定了一个长度为n的数列和若干个询问,每个询问是关于数列的区间表示数列的第1个数到第r个数),首先你要统计该区间内大于等于a,小于等于b的数的个数,其次是所有大于等于a,小于等于b的,且在该区间中出现过的数值的个数。

小A望着那数万的数据规模几乎绝望,只能向大神您求救,请您帮帮他吧。

输入输出格式

输入格式:

第一行n,m

接下来n个数表示数列

接下来m行,每行四个数l,r,a,b

输出格式:

输出m行,分别对应每个询问,输出两个数,分别为在1到i?这段区间中大小在[a,b]中的数的个数,以及大于等于a,小于等于b的,且在该区间中出现过的数值的个数(具体可以参考样例)。

输入输出样例

输入样例#1: 复制

3 4
1 2 2
1 2 1 3
1 2 1 1
1 3 1 3
2 3 2 3
输出样例#1: 复制

2 2
1 1
3 2
2 1
说明

N<=100000,M<=100000

复习莫队..

这题gty的妹子序列 应该几乎差不多

考虑每次询问莫队处理 然后针对权值分块 每次查询sqrt(n)即可完成

没有权值范围差评 差点不敢写 不过写了就A了 还是好评的

#include<cmath>
#include<cstdio>
#include<cctype>
#include<algorithm>
using namespace std;
inline char gc(){
    static char now[1<<16],*S,*T;
    if (T==S){T=(S=now)+fread(now,1,1<<16,stdin);if (T==S) return EOF;}
    return *S++;
}
inline int read(){
    int x=0,f=1;char ch=gc();
    while(!isdigit(ch)) {if (ch=='-') f=-1;ch=gc();}
    while(isdigit(ch)) x=x*10+ch-'0',ch=gc();
    return x*f;
}
const int N=1e6+10;
const int M=1e6+10;
struct node{
    int l,r,a,b,id;
}qr[N];
int sum1[500],sum2[500];// sum1->all sum2-> kind
int ans1[M],ans2[M]; //ans1->all ans2->kind
int b[N],a[N],cnt[N],nn,n,m; 
inline bool cmp(const node &aa,const node &bb){
    return b[aa.l]==b[bb.l]?aa.r<bb.r:b[aa.l]<b[bb.l];
}
inline void change(int p,int v){
    int vv=a[p];cnt[vv]+=v;sum1[b[vv]]+=v;
    if(!cnt[vv]&&v==-1) --sum2[b[vv]];
    if(cnt[vv]==1&&v==1) ++sum2[b[vv]];
}
inline void query(int l,int r,int id){
    if (b[l]==b[r]){
        for (int i=l;i<=r;++i) ans1[id]+=cnt[i],ans2[id]+=cnt[i]>0;return;
    }
    for (int i=l;i<=b[l]*nn;++i)  ans1[id]+=cnt[i],ans2[id]+=cnt[i]>0;
    for (int i=(b[r]-1)*nn+1;i<=r;++i) ans1[id]+=cnt[i],ans2[id]+=cnt[i]>0;
    for (int i=b[l]+1;i<b[r];++i) ans1[id]+=sum1[i],ans2[id]+=sum2[i];
}
int main(){
    freopen("bzoj3236.in","r",stdin);
    n=read();m=read();nn=sqrt(n);
    for (int i=1;i<=n;++i) a[i]=read(),b[i]=(i-1)/nn+1;
    for (int i=1;i<=m;++i){
        qr[i].id=i;qr[i].l=read();qr[i].r=read();qr[i].a=read();qr[i].b=read();
    }sort(qr+1,qr+m+1,cmp);int cl=1,cr=0;
    for (int i=1;i<=m;++i){
        while(cl<qr[i].l) change(cl++,-1);
        while(cl>qr[i].l) change(--cl,1);
        while(cr<qr[i].r) change(++cr,1);
        while(cr>qr[i].r) change(cr--,-1);
        query(qr[i].a,qr[i].b,qr[i].id);
    }for (int i=1;i<=m;++i) printf("%d %d\n",ans1[i],ans2[i]);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值