洛谷P1972 SDOI2009 HH的項鍊

2021-10-08 14:52:38 字數 3294 閱讀 3700

題目描述

p1972 [sdoi2009]hh的項鍊

hh 有一串由各種漂亮的貝殼組成的項鍊。hh 相信不同的貝殼會帶來好運,所以每次散步完後,他都會隨意取出一段貝殼,思考它們所表達的含義。hh 不斷地收集新的貝殼,因此,他的項鍊變得越來越長。

有一天,他突然提出了乙個問題:某一段貝殼中,包含了多少種不同的貝殼?這個問題很難回答…… 因為項鍊實在是太長了。於是,他只好求助睿智的你,來解決這個問題。

輸入格式

一行乙個正整數n

nn,表示項鍊長度。

第二行n

nn個正整數a

ia_i

ai​,表示項鍊中第i

ii個貝殼的種類。

第三行乙個整數m

mm,表示 hh 詢問的個數。

接下來m

mm行,每行兩個整數l,r

l,rl,

r,表示詢問的區間。

輸出格式

輸出m

mm行,每行乙個整數,依次表示詢問對應的答案。

輸出格式樣例

輸入 #1

6

1 2 3 4 3 5

31 2

3 52 6

輸出 #2
224

說明/提示

對於 20%20% 的資料,1≤n

,m

≤5000

1\le n,m\leq 5000

1≤n,m≤

5000

;對於 40%40% 的資料,1≤n

,m≤1

05

1\le n,m\leq 10^5

1≤n,m≤

105;

對於 60%60% 的資料,1≤n

,m≤5

×105

1\le n,m\leq 5\times10^5

1≤n,m≤

5×10

5;對於 100%100% 的資料,1≤n

,m,a

i≤10

6,1≤

l≤r≤

n1\le n,m,a_i \leq 10^6,1\le l \le r \le n

1≤n,m,

ai​≤

106,

1≤l≤

r≤n。

本題可能需要較快的讀入方式,最大資料點讀入資料約 20mb。

解題思路

使用樹狀陣列進行區間求和。

首先對需要訪問的區間進行歸類,按照right相等進行歸類,因為對於區間[left, right]來說,當right值相等時,對於重複的數字,最右側的數字才是真正有效的。舉例:對於序列5 2 5 3 5來說,訪問區間[1,5]和區間[2,5]實質上都會忽略左側重複的1,即實際序列可處理為0 2 0 3 5

其次對資料進行處理,將右側不重複的位置上的數字替換為1,這樣便可利用求區間和來求不重複數字的個數。舉例:上面的序列可替換為0 1 0 1 1,這樣求區間[2,5]中有多少不相同的數字即為求區間和,即為3。

因此,先將訪問區間資料讀入並存起來,使用sort()按照right從小到大進行排序。遍歷排序好的訪問區間資料,當right值發生更改時,更新原有的樹狀陣列。更新過程需要注意:第一,更新過程僅需要從上一次right的下乙個資料開始到本次right值位置結束;第二,為保證原有的重複資料被更新,使用table陣列來記錄每乙個資料的最新位置,舉例:對於序列5 2 5 3 5,在訪問區間[1,3]時有table[5]=3,在訪問區間[2,5]時由於更新,此時table[5]=5同時將樹狀陣列位置3的值更新為0,即將樹狀陣列中table[5]位置的值更新為0;對於沒有重複的資料,直接更新樹狀陣列值為1即可。

**如下:

#include

#include

using

namespace std;

#define max 1000002

int n, m;

int shell[max]

, cp_shell[max]

, table[max]

, res[max]

;//shell存原始資料,cp_shell為樹狀陣列,table記錄資料最後出現的位置,res記錄第i次訪問資料的結果

struct visitvisit[max]

;//記錄訪問資料

intread()

return res;

}bool

cmp(

struct visit a,

struct visit b)

/*---樹狀陣列操作---*/

intlowbit

(int num)

void

update

(int position,

int add_num)

}int

sum(

int position)

return res;

}/*------------------*/

intmain()

m =read()

;for

(int i =

0; i < m; i++

)//按照right進行分類排序

sort

(visit, visit + m, cmp)

;//遍歷訪問資料

int right =

0, next =1;

for(

int i =

0; i < m; i++

) next = right +1;

//記錄下一次更新資料的起始位置為當前right + 1

} res[visit[i]

.num]

=sum

(visit[i]

.right)

-sum

(visit[i]

.left -1)

;//由於對訪問區間結構體進行過排序,因此不直接輸出,而是將結果存入res陣列中

}//按順序輸出

for(

int i =

0; i < m; i++

)return0;

}

參考資料

樹狀陣列詳解

洛谷題解

洛谷P1972 SDOI2009 HH的項鍊

這道題想了很久,發題解是為了理解的更深刻一點。管理放我過好嘛qwq 步入正題 這道題應該是很多做法,我選擇的是離線 樹狀陣列。首先輸入陣列。用fisrt陣列先記錄元素最開始出現的位置,對應的每乙個樹狀陣列的位置add一下 樹狀陣列洛谷也有模板題的了解一下就ok啦 rep i,1 n 然後倒著更新一遍...

洛谷 P1972 SDOI2009 HH的項鍊

1.按每個要求的區間的右端點排序一下 2.樹狀陣列tree j 維護從1到j區間內不同數字的個數有多少個 3.然後用字首和的思想就好 tree r tree l 1 1 include2 include3 include4 define maxn 1000002 5 define next nex ...

洛谷 P1972 SDOI2009 HH的項鍊

p1972 sdoi2009 hh的項鍊 法一 樹狀陣列,離線 翻譯 給出乙個數列a n 還有許多請求,請求由l,r兩個數組成,要求對於每個請求輸出數列中從a l 到a r 中不重複的數的個數。方法 首先讀入數列a n 並預處理next1,boo兩個輔助陣列,方法見程式。然後讀入請求,把請求按l從小...