傳送門!html
彷佛是有兩種方法的,,,因此分別港下好了QAQc++
第一種,莫隊數組
看到這種詢問不少區間之類的就會天然而然地想到莫隊趴?而後仔細思考一下,發現複雜度彷佛是歐克的,並且好像也是能作的,那就試着作下唄ide
首先考慮到怎麼從[l,r]轉移到[l,r+1],能夠想到這個之間的增量=就是區間內最小值之和,因而用個st表搞rmq就好,這裏具體港下QwQspa
首先若是已經求出來了[l,r]內部的最小值的位置pos,這裏不過多闡述了rmq就成了code
那麼分狀況討論下咯htm
若是a[r+1]<a[pos],那就是貢獻a[r+1]*(r-l+1),不說blog
不然就要繼續討論get
顯然[l,pos]的貢獻就都出來了嘛,a[pos]*(pos-l+1)it
而後考慮怎麼求[pos+1,r]的貢獻呢QAQ?
能夠考慮開倆數組lst[i]表示第i個數的左邊第一個比它小的數,能夠先單調棧求出來
再設sum[i]表示第1個數到第i個數的貢獻
顯然能夠獲得sum[i]=sum[lst[i]]+a[i]*(i-lst[i])
而後就能夠獲得總貢獻=a[pos]*(pos-l+1)+sum[r+1]-sum[pos]
而後若是是移動l就再搞個反的就成了,差很少
那接下來不就是莫隊板子了嘛,不說了
#include<bits/stdc++.h> using namespace std; #define il inline #define fr first #define sc second #define rg register #define gc getchar() #define mp make_pair #define ll long long #define rp(i,x,y) for(rg int i=x;i<=y;++i) #define my(i,x,y) for(rg int i=x;i>=y;--i) const ll N=100000+1000; int n,m,blk; int l[N],r[N],s[N],top,a[N],lg[N],poww[N],l_nw,r_nw; ll f[N],g[N],as[N],ret; struct query{int i,l,r,blk;}q[N]; bool operator<(query a,query b){if(a.blk!=b.blk)return a.blk<b.blk;return a.r<b.r;} struct stable { int p[20][N]; il void pre(){rp(j,1,lg[n])rp(i,1,n-poww[j-1])p[j][i]=a[p[j-1][i]]<=a[p[j-1][i+poww[j-1]]]?p[j-1][i]:p[j-1][i+poww[j-1]];} il int query(int l,int r){int k=lg[r-l+1];return a[p[k][l]]<=a[p[k][r-poww[k]+1]]?p[k][l]:p[k][r-poww[k]+1];} }st; il int read() { rg char ch=gc;rg int x=0;rg bool y=1; while(ch!='-' && (ch>'9' || ch<'0'))ch=gc; if(ch=='-')ch=gc,y=0; while(ch>='0' && ch<='9')x=(x<<1)+(x<<3)+(ch^'0'),ch=gc; return y?x:-x; } il ll calcl(int l,int r){int pos=st.query(l,r);return 1ll*(r-pos+1)*a[pos]+g[l]-g[pos];} il ll calcr(int l,int r){int pos=st.query(l,r);return 1ll*(pos-l+1)*a[pos]+f[r]-f[pos];} il void pre(){poww[0]=1;poww[1]=2;lg[1]=0;rp(i,2,n)poww[i]=poww[i-1]<<1,lg[i]=lg[i>>1]+1;} int main() { // freopen("xl.in","r",stdin);freopen("xl.out","w",stdout); n=read();m=read();blk=sqrt(n);pre(); rp(i,1,n)a[i]=read(); rp(i,1,n){while(top&&a[s[top]]>a[i])--top;l[i]=s[top];s[++top]=i;}rp(i,1,n)f[i]=f[l[i]]+1ll*(i-l[i])*a[i]; s[top=0]=n+1; my(i,n,1){while(top&&a[s[top]]>a[i])--top;r[i]=s[top];s[++top]=i;}my(i,n,1)g[i]=g[r[i]]+1ll*(r[i]-i)*a[i]; rp(i,1,n)st.p[0][i]=i;st.pre(); rp(i,1,m){int l=read(),r=read();q[i]=(query){i,l,r,(l-1)/blk};}sort(q+1,q+1+m);l_nw=q[1].l;r_nw=l_nw-1; rp(i,1,m) { while(r_nw<q[i].r)ret+=calcr(l_nw,++r_nw); while(l_nw>q[i].l)ret+=calcl(--l_nw,r_nw); while(r_nw>q[i].r)ret-=calcr(l_nw,r_nw--); while(l_nw<q[i].l)ret-=calcl(l_nw++,r_nw); as[q[i].i]=ret; } rp(i,1,m)printf("%lld\n",as[i]); return 0; }
第二種,線段樹+掃描線
其實和影魔差很少來着,,,
因此爲何我麻油作出來呢QAQ
說明是真的落實很不紮實,很不該該
而後說下思路,,,我想了很久才get,,,真的仍是要認真落實,,,否則吃棗藥丸,,,
其實真的和影魔差很少了,,,只是一個要維護最大值一個要維護最小值來着QAQ
因此這題就同樣的思路,考慮對棧維護一棵線段樹,對整個兒序列維護一棵線段樹
先放下個人淺薄理解QAQ
考慮for循環枚舉右端點,而後每次對於右端點在當前枚舉點的就能夠直接求值了
而後如今是維護了一個單調棧嘛,不難想到對於每一個子區間,答案能夠分紅兩個部分
考慮找到單調棧中小於等於這個區間的右端點r的最大元素的位置i,對於單調棧中位置在i及其右邊的點,貢獻是必定的,就是∑a[stck[i]]*(stck[i]-stck[i-1]),並且這個顯然是能夠給棧開一個線段樹維護的,就每次棧中加入新元素的時候說明當前點能延伸到的最右已經固定了(以後被彈走什麼的一下子另說QAQ),因此就能夠對這個棧的線段樹上加上這個值
可是對於i+1到r的值,若是另外求,複雜度依然過不去
考慮到在i到右端點原本也是有個最大值的,只是以後被棧中的i+1那個點給彈走了
因此咱們能夠在每一個棧中元素被彈走的時候再維護另一個線段樹,這樣就能夠分別求出兩個部分的ans,而後相加就好
大概就是醬嬸兒的,而後等下放代碼QAQ