1. 程式人生 > >[AHOI2013]作業

[AHOI2013]作業

數值 IV num 以及 style 次數 osi its 輸入輸出格式

題目描述

此時己是淩晨兩點,剛剛做了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\n",ans,aans); 22 } 23 return 0; 24 }

正解:

做法不唯一,常見做法為莫隊+分塊或者莫隊+樹狀數組,但是網上也有一些奇奇怪怪的方法。

求任意一段區間內在[a,b]的數字個數,很容易想到分塊;但是題目有要求該區間內在[a,b]的數值種數,這又很容易聯想到莫隊。所以正解就是莫隊+分塊。例如樣例的第一個詢問,莫隊增加在這個區間內每個數字出現的次數、該數字所在的塊的元素個數(包括重復的數字)、該數所在塊的不重復元素個數。然後分塊詢問[a,b]之間在規定區間的答案。

正解很巧妙地一點在於分塊分的不僅僅是有多少個數(即n),還是數值(即color[ ])。這也就存在一個問題,假設n很小,但是color[ ]很大又要另做處理。

如果不能理解代碼的建議自行模擬樣例。

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 const int N=100010;
 4 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;
 5 struct node{
 6     int l,r,x,y,id;
 7 }a[N];
 8 bool cmp(node aa,node bb)
 9 {
10     if(posi[aa.l]==posi[bb.l]) return aa.r<bb.r;
11     else return aa.l<bb.l;
12 }
13 void build()//預處理出每個塊的邊界
14 {
15     block=sqrt(n);num=n/block;
16     if(n%block) num++;
17     for(int i=1;i<=num;i++) 
18     lll[i]=(i-1)*block+1,rrr[i]=i*block;
19     rrr[num]=n;
20     for(int i=1;i<=n;i++) posi[i]=(i-1)/block+1;
21 }
22 void add(int col)
23 {
24     sum[col]++;skuai[posi[col]]++;
25     if(sum[col]==1) change[posi[col]]++;
26 }
27 void del(int col)
28 {
29     sum[col]--;skuai[posi[col]]--;
30     if(!sum[col]) change[posi[col]]--;
31 }
32 void find(int l,int r,int id)
33 {
34     if(posi[l]==posi[r]) 
35     {
36         for(int i=l;i<=r;i++) if(sum[i]) ans1[id]+=sum[i],ans2[id]++;
37         return;
38     }
39     for(int i=l;i<=rrr[posi[l]];i++) if(sum[i]) ans1[id]+=sum[i],ans2[id]++;
40     for(int i=lll[posi[r]];i<=r;i++) if(sum[i]) ans1[id]+=sum[i],ans2[id]++;
41     for(int i=posi[l]+1;i<posi[r];i++) ans1[id]+=skuai[i],ans2[id]+=change[i];
42 }
43 int main()
44 {
45     scanf("%d%d",&n,&m);
46     for(int i=1;i<=n;i++) scanf("%d",&color[i]);
47     build();
48     for(int i=1;i<=m;i++)
49     {
50         scanf("%d%d%d%d",&a[i].l,&a[i].r,&a[i].x,&a[i].y);
51         a[i].id=i;
52     }
53     L=1,R=0;
54     sort(a+1,a+1+m,cmp);
55     for(int i=1;i<=m;i++)
56     {
57         while(R<a[i].r) add(color[++R]);
58         while(R>a[i].r) del(color[R--]);
59         while(L<a[i].l) del(color[L++]);
60         while(L>a[i].l) add(color[--L]);
61         find(a[i].x,a[i].y,a[i].id);
62     }
63     for(int i=1;i<=m;i++)
64         printf("%d %d\n",ans1[i],ans2[i]);
65     return 0;
66 }

[AHOI2013]作業