6854. 【2020.11.04提高組模擬】古老的序列問題
阿新 • • 發佈:2020-11-05
一個數列,若干次詢問,每次詢問\([l,r]\)區間中所有子區間的\(max*min\)的和。
\(n,Q\le 10^5\)
老套路,維護個單調遞增的棧和單調遞減的棧,列舉右端點,維護每個左端點的貢獻。搞個歷史和,但是由於這裡維護的是乘積,所以要維護各種各樣的資訊,比較複雜。反正我是沒有寫出來。
另一種做法:分治,將每個詢問拆成\(O(\log n)\)個子問題,表示一個區間\([l,r]\)中,左端點在左半邊,右端點在右半邊的貢獻。
對右區間搞個字首\(max\)和\(min\),列舉左端點,求出左邊的最大值和最小值,計算最值在右邊的分界點(左邊用左半邊的最值,右邊用右半邊的字首最值),這樣就可以分成三個區間(實際上是四種區間)。對這四種區間分別用資料結構維護,支援區間加係數和區間詢問和。直接樹狀陣列即可。
時間\(O(n\lg^2 n)\)
using namespace std; #include <cstdio> #include <cstring> #include <algorithm> #include <vector> #define N 100005 #define ll long long #define mo 1000000007 #define INF 1000000000 int n,m; ll s[N]; struct Query{int l,r,t;}; vector<Query> q[N*4]; bool cmpp(Query x,Query y){return x.l>y.l;} ll f[N*4],ans[N]; struct TA{ int len; ll t[N]; void init(int _n=n){ len=_n; memset(t,0,sizeof(ll)*(len+1)); } void add(int x,ll c){ if (!c) return; for (;x<=len;x+=x&-x) (t[x]+=c)%=mo; } ll query(int x){ ll res=0; for (;x;x-=x&-x) res+=t[x]; return res%mo; } }; struct TA2{ int len,offset; TA t0,t1; ll p[N]; // ll s[N]; void init(int _offset,int _n=n,ll *_p=NULL){ offset=_offset,len=_n; t0.init(len),t1.init(len); // if (_p==NULL) for (int i=1;i<=len;++i) p[i]=1; // else for (int i=1;i<=len;++i) p[i]=_p[i+offset]; // memset(s,0,sizeof(ll)*(len+1)); if (_p==NULL) for (int i=1;i<=len;++i) p[i]=i; else for (int i=1;i<=len;++i) p[i]=(p[i-1]+_p[i+offset])%mo; } void add(int l,int r,ll c){ if (l>r) return; l-=offset,r-=offset; // for (int i=l;i<=r;++i) // (s[i]+=c)%=mo; t1.add(l,c),t1.add(r+1,mo-c); t0.add(l,mo-c*p[l-1]%mo); t0.add(r+1,c*p[r]%mo); } ll query(int r){ r-=offset; // ll res=0; // for (int i=1;i<=r;++i) // (res+=p[i]*s[i])%=mo; // return res; return (t1.query(r)*p[r]+t0.query(r))%mo; } } _,A,B,AB; void divide(int k,int l,int r){ if (l==r){ f[k]=s[l]*s[l]%mo; for (int i=0;i<q[k].size();++i) (ans[q[k][i].t]+=f[k])%=mo; q[k].clear(); return; } static vector<Query> p; p.clear(); int mid=l+r>>1; for (int i=0;i<q[k].size();++i) if (q[k][i].l<=mid && mid<q[k][i].r && !(q[k][i].l==l && q[k][i].r==r)) p.push_back(q[k][i]); sort(p.begin(),p.end(),cmpp); static ll a[N],b[N],ab[N]; a[mid]=INF,b[mid]=-INF; for (int i=mid+1;i<=r;++i){ a[i]=min(a[i-1],s[i]); b[i]=max(b[i-1],s[i]); ab[i]=a[i]*b[i]%mo; } int len=r-mid; _.init(mid,len),A.init(mid,len,a),B.init(mid,len,b),AB.init(mid,len,ab); int al=mid,bl=mid; ll la=INF,lb=-INF; for (int i=mid,j=0;i>=l;--i){ la=min(la,s[i]),lb=max(lb,s[i]); for (;al<r && a[al+1]>la;++al); for (;bl<r && b[bl+1]<lb;++bl); _.add(mid+1,min(al,bl),la*lb%mo); if (bl<al) B.add(min(al,bl)+1,al,la); if (al<bl) A.add(min(al,bl)+1,bl,lb); AB.add(max(al,bl)+1,r,1); for (;j<p.size() && p[j].l==i;++j) (ans[p[j].t]+=_.query(p[j].r)+A.query(p[j].r)+B.query(p[j].r)+AB.query(p[j].r))%=mo; } (f[k]+=_.query(r)+A.query(r)+B.query(r)+AB.query(r))%=mo; p.clear(); for (int i=0;i<q[k].size();++i){ int L=q[k][i].l,R=q[k][i].r; if (L==l && R==r) continue; if (R<=mid) q[k<<1].push_back(q[k][i]); else if (L>mid) q[k<<1|1].push_back(q[k][i]); else{ q[k<<1].push_back((Query){L,mid,q[k][i].t}); q[k<<1|1].push_back((Query){mid+1,R,q[k][i].t}); } } divide(k<<1,l,mid); divide(k<<1|1,mid+1,r); f[k]+=f[k<<1]+f[k<<1|1]; for (int i=0;i<q[k].size();++i) if (q[k][i].l==l && q[k][i].r==r) (ans[q[k][i].t]+=f[k])%=mo; q[k].clear(); // printf("f[%d]=%lld\n",k,f[k]); } int main(){ // freopen("in.txt","r",stdin); // freopen("out.txt","w",stdout); freopen("sequence.in","r",stdin); freopen("sequence.out","w",stdout); scanf("%d%d",&n,&m); for (int i=1;i<=n;++i) scanf("%lld",&s[i]); for (int i=1;i<=m;++i){ int l,r; scanf("%d%d",&l,&r); q[1].push_back((Query){l,r,i}); } divide(1,1,n); for (int i=1;i<=m;++i) printf("%lld\n",ans[i]); return 0; }