Hnoi2016 序列(莫隊 st表)

2021-08-20 20:26:46 字數 3950 閱讀 3388

給定長度為

n n

的序列:a1

' role="presentation" style="position: relative;">a1a

1,a2

a

2,…,an

a

n,記為a[

1:n]

a [1

:n

]。類似地,a[

l:r]

a [l

:r

](1≤

l≤r≤

n 1≤l

≤r≤n

)是指序列:al

a

l,al

+1a l+

1,…,ar

−1a r−

1,ar

a

r。若1≤

l≤s≤

t≤r≤

n 1≤l

≤s≤t

≤r≤n

,則稱a[

s:t]

a [s

:t

]是a[

l:r]

a [l

:r

]的子串行。現在有

q q

個詢問,每個詢問給定兩個數

l' role="presentation" style="position: relative;">ll和

r r

,1≤l≤r≤

n' role="presentation" style="position: relative;">1≤l

≤r≤n

1≤l≤

r≤n,求

a[l:

r]a [l

:r

]的不同子串行的最小值之和。例如,給定序列5,2,4,1,3,詢問給定的兩個數為1和3,那麼a[

1:3]

a [1

:3

]有6個子序列a[

1:1]

,a[2

:2],

a[3:

3],a

[1:2

],a[

2:3]

,a[1

:3] a[1

:1],

a[2:

2],a

[3:3

],a[

1:2]

,a[2

:3],

a[1:

3]

,這6個子序列的最小值之和為5+2+4+2+2+2=17。

輸入檔案的第一行包含兩個整數

n n

和q' role="presentation" style="position: relative;">q

q,分別代表序列長度和詢問數。接下來一行,包含

n n

個整數,以空格隔開,第

i' role="presentation" style="position: relative;">i

i個整數為ai

a

i,即序列第

i i

個元素的值。接下來

q' role="presentation" style="position: relative;">q

q行,每行包含兩個整數

l l

和r' role="presentation" style="position: relative;">r

r,代表一次詢問。

對於每次詢問,輸出一行,代表詢問的答案。

sample input

5 5 

5 2 4 1 3

1 5

1 3

2 4

3 5

2 5

sample output
28 

17 11

11 17

hint 1≤

n,q≤

100000,|

ai|≤

1091 ≤n

,q

≤100000,|

ai|≤

109

' role="presentation" style="position: relative;">

' role="presentation" style="position: relative;">

' role="presentation" style="position: relative;">

' role="presentation" style="position: relative;">

' role="presentation" style="position: relative;">

' role="presentation" style="position: relative;">

聽說可以大力上線段樹?可是我不會啊。

其實莫隊也不怎麼會。

還是說說怎麼做吧。首先莫隊的方法可以把這個問題變成對於區間[l,r],以r為右端點,再求答案。而且由於莫隊的複雜度,我們需要做到o(

1)o (1

)怎麼求答案?本來想的是存乙個每個點左右比它小的第乙個數,結果發現似乎變成了乙個遞迴的子問題,感覺還是很無解。

然後在網上看到一種很巧妙的做法。我們現在有區間[l,r]的答案,向左擴充套件一位。首先我們找出[l,r+1]中最小的數k,k左邊作為左端點那麼對答案有貢獻的是k。然後我們需要計算[k+1,r],發現乙個特點,[k+1,r]中比k都大。我們預處理以i為右端點的區間的答案和s[i]。由於k及k左側的貢獻都會是k左邊的數,所以在s[k+1]和s[r]是可以相減的。我們只需要減一減就好了。

rmq是o(1)的。

莫隊可能不支援除100,調成200就過了,不知道為什麼。

code:

#include

#include

#include

#include

#include

using

namespace

std;

struct lxyyyy;

struct lxy1

}b[100005];

int l[100005],r[100005],a[100005];

long

long fl[100005],fr[100005],now,ans[100005];

int st[200005][17],lg[100005];

int n,m,lnt,rnt;

stack

d;int readit()

while(s>='0'&&s<='9')

return f*g;

}int findit(int x,int y)

void lml()

void lmr()

void rml()

void rmr()

int main()

for(register

int i=1;i<=16;i++)

for(register

int j=1;j<=n;j++)

for(register

int i=1;i<=n;i++)

while(!d.empty()) d.pop();

for(register

int i=n;i>=1;i--)

for(register

int i=1;i<=m;i++)

scanf("%d%d",&b[i].l,&b[i].r),b[i].tim=i;

sort(b+1,b+1+m);

for(register

int i=1;i<=n;i++)

for(register

int i=n;i>=1;i--)

lnt=1,rnt=1,now=a[1];

for(register

int i=1;i<=m;i++)

題解 HNOI 2016序列

collapse bzoj 這道題在hnoi2016中還算是好的了 這題中如若去掉多組詢問的話可以在o nlogn o n log n 的時間內得解 並查集 但多組詢問必定要優化,發現這種其他結構基本上無法涉足的題目就只能上莫隊了 我也不知道為啥想到莫隊,可能這就是題感吧 減去o nn o n n ...

Hnoi2016 序列 解題報告

我們考慮從左往右掃右端點和從右往左掃左端點的兩遍掃瞄線。以下選取從左往右的掃瞄線來說明 考慮每個點向它左邊第乙個比它大的點連邊形成的樹。設i左邊第乙個比它大的點的座標是la sti 如果沒有則la sti 0 i右邊第乙個比它大的點的座標是ne xti 如果沒有則ne xti n 1 設 l,r 的...

HNOI2016 序列(未通過)

題解 雖然知道有點問題但是並沒有debug出來 發現錯誤了。相同元素的處理有錯誤 網上題解大都是分塊。hn怎麼道道分塊 用最普通的思路,可以列舉每個點作為最小值,向左向右延伸 但是多組詢問顯然我們是要去優化詢問過程的 有一種方法就是先找出最大值 其實也可以是隨意乙個位置吧,但yy一下應該最大值能擴充...