题目描述
此时己是凌晨两点,刚刚做了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
暴力做法77分:
1 #include<bits/stdc++.h> 2 using namespace std; 3 int n,m,c[100010]; 4 bool p[10000000]; 5 int main() 6 { 7 scanf("%d%d",&n,&m); 8 for(int i=1;i<=n;i++) scanf("%d",&c[i]); 9 int l,r,a,b,ans,aans; 10 while(m--) 11 { 12 ans=0;aans=0; 13 scanf("%d%d%d%d",&l,&r,&a,&b); 14 for(int i=l;i<=r;i++) 15 if(c[i]>=a&&c[i]<=b) 16 { 17 ++ans; 18 if(!p[c[i]]) p[c[i]]=1,++aans; 19 } 20 for(int i=l;i<=r;i++) p[c[i]]=0; 21 printf("%d %d ",ans,aans); 22 } 23 return 0; 24 }
正解:
做法不唯一,常见做法为莫队+分块或者莫队+树状数组,但是网上也有一些奇奇怪怪的方法。
求任意一段区间内在[a,b]的数字个数,很容易想到分块;但是题目有要求该区间内在[a,b]的数值种数,这又很容易联想到莫队。所以正解就是莫队+分块。例如样例的第一个询问,莫队增加在这个区间内每个数字出现的次数、该数字所在的块的元素个数(包括重复的数字)、该数所在块的不重复元素个数。然后分块询问[a,b]之间在规定区间的答案。
正解很巧妙地一点在于分块分的不仅仅是有多少个数(即n),还是数值(即color[ ])。这也就存在一个问题,假设n很小,但是color[ ]很大又要另做处理。
如果不能理解代码的建议自行模拟样例。
更新:针对hack数据改了一波,之前的问题是如果l或r超出了color[]的最大值,posi[l]或者posi[r]会 变成0,那么对答案的统计就会出现问题
1 /*hack.in: 5 1 1 1 2 5 2 3 5 3 10 hack.out: 1 1*/ 2 #include<bits/stdc++.h> 3 using namespace std; 4 const int N=100010; 5 int n,m,L,R,block,num,posi[N],lll[N],rrr[N],sum[N],skuai[N],change[N],color[N],ans1[N],ans2[N],anss1,anss2; 6 struct node{ 7 int l,r,x,y,id; 8 }a[N]; 9 bool cmp(node aa,node bb) 10 { 11 if(posi[aa.l]==posi[bb.l]) return aa.r<bb.r; 12 else return aa.l<bb.l; 13 } 14 void build()//预处理出每个块的边界 15 { 16 block=sqrt(n);num=n/block; 17 if(n%block) num++; 18 for(int i=1;i<=num;i++) 19 lll[i]=(i-1)*block+1,rrr[i]=i*block; 20 rrr[num]=n; 21 for(int i=1;i<=n;i++) posi[i]=(i-1)/block+1; 22 } 23 void add(int col) 24 { 25 sum[col]++;skuai[posi[col]]++; 26 if(sum[col]==1) change[posi[col]]++; 27 } 28 void del(int col) 29 { 30 sum[col]--;skuai[posi[col]]--; 31 if(!sum[col]) change[posi[col]]--; 32 } 33 void find(int l,int r,int id) 34 { 35 if(posi[l]==posi[r]) 36 { 37 for(int i=l;i<=r;i++) if(sum[i]) ans1[id]+=sum[i],ans2[id]++; 38 return; 39 } 40 if(posi[l]!=0) 41 for(int i=l;i<=rrr[posi[l]];i++) if(sum[i]) ans1[id]+=sum[i],ans2[id]++; 42 if(posi[r]!=0) 43 for(int i=lll[posi[r]];i<=r;i++) if(sum[i]) ans1[id]+=sum[i],ans2[id]++; 44 if(posi[l]!=0&&posi[r]!=0) 45 for(int i=posi[l]+1;i<posi[r];i++) ans1[id]+=skuai[i],ans2[id]+=change[i]; 46 if(posi[l]!=0&&posi[r]==0) 47 for(int i=posi[l]+1;i<=num;i++) ans1[id]+=skuai[i],ans2[id]+=change[i]; 48 } 49 int main() 50 { 51 scanf("%d%d",&n,&m); 52 for(int i=1;i<=n;i++) scanf("%d",&color[i]); 53 build(); 54 for(int i=1;i<=m;i++) 55 { 56 scanf("%d%d%d%d",&a[i].l,&a[i].r,&a[i].x,&a[i].y); 57 a[i].id=i; 58 } 59 L=1,R=0; 60 sort(a+1,a+1+m,cmp); 61 for(int i=1;i<=m;i++) 62 { 63 while(R<a[i].r) add(color[++R]); 64 while(R>a[i].r) del(color[R--]); 65 while(L<a[i].l) del(color[L++]); 66 while(L>a[i].l) add(color[--L]); 67 find(a[i].x,a[i].y,a[i].id); 68 } 69 for(int i=1;i<=m;i++) 70 printf("%d %d ",ans1[i],ans2[i]); 71 return 0; 72 }