給定長度為
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 output28
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一下應該最大值能擴充...