[提高組集訓2021] 古老的序列問題
阿新 • • 發佈:2021-10-03
一、題目
有一個長度為 \(n\) 的整數序列 \(a\),你需要回答 \(m\) 個詢問,每次給出 \(L,R\),求下列式子的值:
\[\sum_{l=L}^R\sum_{r=l}^R(\max_{i=l}^r s_i)\cdot (\min_{i=l}^rs_i) \]\(n,m\leq 10^5\)
二、解法
解法一
貓樹分治的進階版應用,我們把貓叔看作線段樹,每次把詢問在貓樹上面下放,下放的方式:如果正好覆蓋當前區間那麼留在這個節點上,回溯時算答案;如果不經過中點那麼直接下放;如果經過中點那麼統計左邊對右邊的貢獻。
主要問題是算左對右的貢獻,我們拿一個指標在左半部分從後往前移動,記錄到中點的最小值和最大值分別是 \(la,lb\)
不難發現可以用雙指標來找到這些段,然後對於右邊的每個點維護左邊的一個字尾對它的貢獻,在移動左邊指標的時候更新貢獻即可,四個段對應的係數不同(如果在右邊去最值那麼要用右邊的來乘,這個叫做係數),那麼我們用四個線段樹來維護係數即可,詢問的時候是一個區間查詢的過程。
那麼時間複雜度 \(O(n\log^2n)\),要注意卡常啊
解法二
如果只有一次詢問怎麼辦?移動右端點維護每個左端點的答案,把右端點貢獻到詢問上去即可。
如果有多個詢問怎麼辦?移動右端點維護每個右端點對應每個左端點的答案,把歷史和貢獻到詢問上去即可。
那麼寫兩個單調棧\(+\)一個帶區間乘法支援區間歷史和查詢的線段樹即可,用矩陣乘法維護歷史和,時間複雜度 \(O(n\log n)\)
三、總結
貓樹進階應用,一言以蔽之:線段樹加 \(cdq\) 分治,注意利用過中點這個性質!
遇事不決,矩陣乘法,矩陣什麼都可以維護!
//cat-tree divide #include <cstdio> #include <vector> #include <iostream> #include <algorithm> using namespace std; const int M = 100005; const int N = 400005; const int MOD = 1e9+7; int read() { int x=0,f=1;char c; while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;} while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();} return x*f; } int n,m,s[M],z[M],a[M],b[M],ab[M],ans[M],f[N]; struct node { int l,r,id; bool operator < (const node &b) const { return l>b.l; } };vector<node> q[N]; struct seg { int L,R,sum[N],sx[N],tag[N]; int mul(int x,int y) {return 1ll*x*y%MOD;} void add(int i,int c) { sum[i]=(sum[i]+mul(sx[i],c))%MOD; tag[i]=(tag[i]+c)%MOD; } void build(int i,int l,int r,int *a) { if(i==1) L=l,R=r; sum[i]=tag[i]=0; if(l==r) { sx[i]=a[l]; return ; } int mid=(l+r)>>1; build(i<<1,l,mid,a); build(i<<1|1,mid+1,r,a); sx[i]=(sx[i<<1]+sx[i<<1|1])%MOD; } void down(int i) { if(!tag[i]) return ; add(i<<1,tag[i]); add(i<<1|1,tag[i]); tag[i]=0; } void ins(int i,int l,int r,int L,int R,int c) { if(L>r || l>R) return ; if(L<=l && r<=R) { add(i,c); return ; } int mid=(l+r)>>1;down(i); ins(i<<1,l,mid,L,R,c); ins(i<<1|1,mid+1,r,L,R,c); sum[i]=(sum[i<<1]+sum[i<<1|1])%MOD; } int ask(int i,int l,int r,int L,int R) { if(L>r || l>R) return 0; if(L<=l && r<=R) return sum[i]; int mid=(l+r)>>1;down(i); return (ask(i<<1,l,mid,L,R)+ ask(i<<1|1,mid+1,r,L,R))%MOD; } int Ask(int x) {return ask(1,L,R,L,x);} }I,A,B,AB; void add(int &x,int y) {x=(x+y)%MOD;} void div(int i,int l,int r) { if(l==r) { f[i]=1ll*s[l]*s[l]%MOD; for(auto x:q[i]) add(ans[x.id],f[i]); return ; } int mid=(l+r)>>1;a[mid]=MOD;b[mid]=0; vector<node> v; for(auto x:q[i]) if(x.l<=mid && x.r>mid && !(x.l==l && x.r==r)) v.push_back(x); sort(v.begin(),v.end()); for(int i=mid+1;i<=r;i++) { z[i]=1; a[i]=min(a[i-1],s[i]); b[i]=max(b[i-1],s[i]); ab[i]=1ll*a[i]*b[i]%MOD; } I.build(1,mid+1,r,z);A.build(1,mid+1,r,a); B.build(1,mid+1,r,b);AB.build(1,mid+1,r,ab); int pa=mid,pb=mid,la=MOD,lb=0,t=mid+1; for(int i=mid,j=0;i>=l;i--) { la=min(la,s[i]);lb=max(lb,s[i]); for(;pa<r && a[pa+1]>la;pa++); for(;pb<r && b[pb+1]<lb;pb++); I.ins(1,t,r,t,min(pa,pb),1ll*la*lb%MOD); if(pa<pb) A.ins(1,t,r,pa+1,pb,lb); if(pb<pa) B.ins(1,t,r,pb+1,pa,la); AB.ins(1,t,r,max(pa,pb)+1,r,1); while(j<v.size() && v[j].l>=i) { node o=v[j]; add(ans[o.id],(1ll*I.Ask(o.r)+A.Ask(o.r) +B.Ask(o.r)+AB.Ask(o.r))%MOD); j++; } } f[i]=(1ll*I.Ask(r)+A.Ask(r)+B.Ask(r)+AB.Ask(r))%MOD; for(auto x:q[i]) { if(l==x.l && x.r==r) continue; if(x.r<=mid) q[i<<1].push_back(x); else if(x.l>mid) q[i<<1|1].push_back(x); else { node t1=x,t2=x; t1.r=mid;q[i<<1].push_back(t1); t2.l=mid+1;q[i<<1|1].push_back(t2); } } div(i<<1,l,mid);div(i<<1|1,mid+1,r); f[i]=(1ll*f[i]+f[i<<1]+f[i<<1|1])%MOD; for(auto x:q[i]) if(l==x.l && x.r==r) add(ans[x.id],f[i]); } void write(int x) { if(x<=9) { putchar(x+'0'); return ; } write(x/10); putchar(x%10+'0'); } signed main() { freopen("sequence.in","r",stdin); freopen("sequence.out","w",stdout); n=read();m=read(); for(int i=1;i<=n;i++) s[i]=read(); for(int i=1;i<=m;i++) { int l=read(),r=read(); q[1].push_back(node{l,r,i}); } div(1,1,n); for(int i=1;i<=m;i++) write(ans[i]),puts(""); }
//segment tree maintaining matrix
#include <cstdio>
#include <vector>
#include <cstring>
#include <cassert>
using namespace std;
const int M = 100005;
const int MOD = 1e9+7;
int read()
{
int x=0,f=1;char c;
while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
return x*f;
}
int n,m,ans[M],a[M],sa[M],sb[M];
struct node{int x,y;};vector<node> g[M];
struct mat
{
int a[2][2];
mat() {memset(a,0,sizeof a);}
void reset() {a[0][1]=a[1][0]=0;a[0][0]=a[1][1]=1;}
mat operator * (const mat &b) const
{
mat r;
for(int i=0;i<2;i++)
for(int j=0;j<2;j++)
for(int k=0;k<2;k++)
r.a[i][k]=(r.a[i][k]+
1ll*a[i][j]*b.a[j][k])%MOD;
return r;
}
}tr[4*M],tag[4*M],ZXY;
int qkpow(int a,int b)
{
int r=1;
while(b>0)
{
if(b&1) r=1ll*r*a%MOD;
a=1ll*a*a%MOD;
b>>=1;
}
return r;
}
mat con(int c)
{
mat r;
r.a[0][0]=c;r.a[1][1]=1;
return r;
}
void build(int i,int l,int r)
{
tag[i].reset();
tr[i].a[0][0]=r-l+1;
if(l==r) return ;
int mid=(l+r)>>1;
build(i<<1,l,mid);
build(i<<1|1,mid+1,r);
}
void down(int i)
{
if(tag[i].a[0][0]==1 && tag[i].a[0][1]==0
&& tag[i].a[1][0]==0 && tag[i].a[1][1]==1) return ;
tr[i<<1]=tr[i<<1]*tag[i];
tag[i<<1]=tag[i<<1]*tag[i];
tr[i<<1|1]=tr[i<<1|1]*tag[i];
tag[i<<1|1]=tag[i<<1|1]*tag[i];
tag[i].reset();
}
void add(int i,int l,int r,int L,int R,mat c)
{
if(L>r || l>R) return ;
if(L<=l && r<=R)
{
tr[i]=tr[i]*c;
tag[i]=tag[i]*c;
return ;
}
int mid=(l+r)>>1;down(i);
add(i<<1,l,mid,L,R,c);
add(i<<1|1,mid+1,r,L,R,c);
tr[i].a[0][0]=(tr[i<<1].a[0][0]
+tr[i<<1|1].a[0][0])%MOD;
tr[i].a[0][1]=(tr[i<<1].a[0][1]
+tr[i<<1|1].a[0][1])%MOD;
}
int ask(int i,int l,int r,int L,int R)
{
if(l>R || L>r) return 0;
if(L<=l && r<=R) return tr[i].a[0][1];
int mid=(l+r)>>1;down(i);
return (ask(i<<1,l,mid,L,R)+
ask(i<<1|1,mid+1,r,L,R))%MOD;
}
void write(int x)
{
if(x<=9)
{
putchar(x+'0');
return ;
}
write(x/10);
putchar(x%10+'0');
}
signed main()
{
freopen("sequence.in","r",stdin);
freopen("sequence.out","w",stdout);
n=read();m=read();
for(int i=1;i<=n;i++)
a[i]=read();
build(1,1,n);
for(int i=1;i<=m;i++)
{
int l=read(),r=read();
g[r].push_back(node{l,i});
}
int la=0,lb=0;mat I;
I.reset();I.a[0][1]=1;
for(int i=1;i<=n;i++)
{
while(la && a[sa[la]]<a[i])
{
int c=qkpow(a[sa[la]],MOD-2);
add(1,1,n,sa[la-1]+1,sa[la],con(c));
la--;
}
while(lb && a[sb[lb]]>a[i])
{
int c=qkpow(a[sb[lb]],MOD-2);
add(1,1,n,sb[lb-1]+1,sb[lb],con(c));
lb--;
}
add(1,1,n,sa[la]+1,i,con(a[i]));
add(1,1,n,sb[lb]+1,i,con(a[i]));
sa[++la]=i;sb[++lb]=i;
add(1,1,n,1,i,I);
for(auto t:g[i])
ans[t.y]=(ans[t.y]+ask(1,1,n,t.x,i))%MOD;
}
for(int i=1;i<=m;i++)
write(ans[i]),puts("");
}