题目大意:
给定一个长度为n的序列,m个询问,询问一段区间内大于等于a小于等于b的数的个数和数值的个数。
n=1e5,m=1e6
题目分析:
看到大于等于a小于等于b这个条件,我们可以想到用树状数组来维护区间内比每个数字小的数的个数(数值同理,判断一下就好惹),每次修改都是logn的,并且可以发现,在原有的序列中新加入或减去一个数字,只需要一次修改,也就符合了莫队算法需要的知道[l,r]的答案,就能快速得到[l+1,r],[l-1,r],[l,r+1],[l,r-1]的答案的性质。但是询问数仍然很大,所以我们考虑分块,把时间复杂度降一个根号,就过了。
时间复杂度O(msqrt(n)logn)
不过我写的莫队真是很辣鸡啊,华丽丽地卡了评测……
代码如下:
#include<cstdio>
#include<algorithm>
#include<cmath>
#define N 120000
#define M 1200000
using namespace std;
struct query{
int l,r,a,b,num;
bool operator < (const query&) const;
}q[M];
int n,m,blocks;
int a[N],belong[N];
int c[N],d[N],cnt[N];
int ans1[M],ans2[M];
int lowbit(int c) {return c&-c;}
bool query :: operator < (const query &c) const{
return belong[l]<belong[c.l] || (belong[l]==belong[c.l] && r<c.r);
}
void update(int x,int y)
{
cnt[x]+=y;
for(int t=x;t<=n;t+=lowbit(t)) c[t]+=y;
if((y==1 && cnt[x]==1) || (y==-1 && cnt[x]==0) )
for(int t=x;t<=n;t+=lowbit(t)) d[t]+=y;
return;
}
void query(int a,int b,int &ans1,int &ans2)
{
for(;b;b-=lowbit(b)) ans1+=c[b],ans2+=d[b];
for(a=a-1;a;a-=lowbit(a)) ans1-=c[a],ans2-=d[a];
return;
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
for(int i=1;i<=m;i++) scanf("%d%d%d%d",&q[i].l,&q[i].r,&q[i].a,&q[i].b),q[i].num=i;
blocks=(int)ceil(sqrt(n));
for(int i=1;i<=n;i++) belong[i]=(i-1)/blocks+1;
sort(q+1,q+1+m);
int l=1,r=0;
for(int i=1;i<=m;i++)
{
while(r<q[i].r) update(a[++r], 1);
while(r>q[i].r) update(a[r--],-1);
while(l<q[i].l) update(a[l++],-1);
while(l>q[i].l) update(a[--l], 1);
query(q[i].a,q[i].b,ans1[q[i].num],ans2[q[i].num]);
}
for(int i=1;i<=m;i++) printf("%d %d\n",ans1[i],ans2[i]);
return 0;
}