bzoj P5016[Snoi2017]一個簡單的詢問——solution

Description

給你一個長度爲N的序列ai,1≤i≤N和q組詢問,每組詢問讀入l1,r1,l2,r2,需輸出
 
get(l,r,x)表示計算區間[l,r]中,數字x出現了多少次。

 

Input

第一行,一個數字N,表示序列長度。
第二行,N個數字,表示a1~aN
第三行,一個數字Q,表示詢問個數。
第4~Q+3行,每行四個數字l1,r1,l2,r2,表示詢問。
N,Q≤50000
N1≤ai≤N
1≤l1≤r1≤N
1≤l2≤r2≤N
注意:答案有可能超過int的最大值
 
                         -by bzoj
http://www.lydsy.com/JudgeOnline/problem.php?id=5016


一開始看錯了題,覺得是每次詢問一個x,
而後想直接主席樹;
而後很快發現題不會這麼簡單;
詢問兩個區間對應顏色出現次數相乘再求和;
兩個區間,顏色;
想到離線
把一個有兩個區間的詢問變成兩個有一個區間和一個前綴的詢問做差,
而後按前綴端點排序,逐漸右移端點,維護更長的前綴,以更新詢問,
這樣每次前綴的端點右移時,前綴裏只有一個顏色的數量增長了1,
相應的,之後要詢問的區間只有這個顏色對答案的貢獻增長了區間中這個顏色的個數,
然而這個顏色對答案的貢獻取決於詢問的區間中有多少這個顏色,
換言之取決於詢問哪一個區間,
一開始想用什麼主席樹之類的維護,死活不會,
而後一看時限3S想到分塊暴力,
而後發現很科學;
而後就有了這個作法
把詢問(l1,r1,l2,r2)拆成(l1,r1,pos=l2-1,-),(l1,r1,pos=r2,+)
表示
詢問[l1,r1]與l2-1前綴的結果,並在原詢問中減去它
詢問[l1,r1]與r2前綴的結果,並在原詢問中加上它
將拆成來的詢問按pos從小到大排序,
按這樣的順序處理詢問能夠經過單向右移端點來依次維護全部詢問的前綴
查詢的時候,採用分塊,
被整塊查詢的塊能夠直接使用一個塊在當前前綴下的答案
維護方法是
在讀入數列時對每一個塊建一個顏色桶MP[]
每右移前綴端點,使顏色col(x)個數在前綴中增長時,把每一個塊的答案都增長MP[col(x)]
被查詢的散點,每一個散點對答案的貢獻是當前前綴中與這個散點顏色相同的點的個數
能夠開個桶,在前綴變長時逐漸更新便可,
複雜度爲$O((N+M)\sqrt{N})$
代碼:
 1 #include<cmath>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<algorithm>
 5 #define LL long long
 6 using namespace std;
 7 int sz,N,Q,num;
 8 int a[50010];
 9 LL MAP[233][50010],inlineMAP[50010];
10 LL ANS[233];
11 struct ss{
12     int L,R,pos,id;
13     LL flag;
14 }qrr[100010];
15 LL ans[50010];
16 bool cmp(ss a,ss b){
17     return a.pos<b.pos;
18 }
19 LL get(int ,int );
20 int main()
21 {
22     int i,j,k;
23     scanf("%d",&N);
24     sz=sqrt(N);
25     for(i=num=1;i<=N;i+=sz,num++){
26         for(j=i;j<i+sz&&j<=N;j++){
27             scanf("%d",&a[j]);
28             MAP[num][a[j]]++;
29         }
30     }
31     num--;
32     scanf("%d",&Q);
33     for(i=1;i<=Q;i++){
34         scanf("%d%d%d%d",&qrr[i].L,&qrr[i].R,&qrr[i].pos,&qrr[i+Q].pos);
35         qrr[i+Q].L=qrr[i].L,qrr[i+Q].R=qrr[i].R;
36         qrr[i+Q].id=qrr[i].id=i;
37         qrr[i].flag=-1,qrr[i+Q].flag=1;
38         qrr[i].pos--;
39     }
40     sort(qrr+1,qrr+Q+Q+1,cmp);
41     j=0;
42     for(i=1;i<=Q<<1;i++){
43         while(j<qrr[i].pos){
44             j++;
45             inlineMAP[a[j]]++;
46             for(k=1;k<=num;k++)
47                 ANS[k]+=MAP[k][a[j]];
48         }
49         ans[qrr[i].id]+=qrr[i].flag*get(qrr[i].L,qrr[i].R);
50     }
51     for(i=1;i<=Q;i++)
52         printf("%lld\n",ans[i]);
53     return 0;
54 }
55 LL get(int L,int R){
56     LL ret=0;
57     int i,j,k;
58     int l,r;
59     if(R-L+1<=sz){
60         for(i=L;i<=R;i++)
61             ret+=inlineMAP[a[i]];
62         return ret;
63     }
64     for(i=j=1;i<L;i+=sz,j++);
65     l=i-1;
66     for(;i+sz<=R+1&&i<=N;j++,i+=sz)
67         ret+=ANS[j];
68     r=i;
69     for(i=L;i<=l;i++)
70         ret+=inlineMAP[a[i]];
71     for(i=r;i<=R;i++)
72         ret+=inlineMAP[a[i]];
73     return ret;
74 }

或許有樹套樹的作法?php

相關文章
相關標籤/搜索