【题目】
BZOJ
给定一个长度为
n
n
n的序列
a
a
a,
Q
Q
Q次询问一段区间中所有连续子序列的最小值之和。
n
,
Q
≤
1
0
5
,
a
i
≤
1
0
9
n,Q\leq 10^5,a_i\leq 10^9
n,Q≤105,ai≤109
【解题思路】
这个标记是真的神奇啊。
不妨令 f i , j f_{i,j} fi,j表示左端点为 i i i,右端点为 j j j的区间最小值。如果我们枚举右端点来更新这个数组,不难发现就是一个区间覆盖。至于覆盖的范围可以先通过单调栈求出来。
对于一个询问 ( l , r ) (l,r) (l,r),其答案就是右端点枚举到 r r r时,历史所有区间 s u m [ l , r ] sum[l,r] sum[l,r]的和。
这个是可以用标记来维护的,具体我们对每个节点维护信息
- l l l:区间长度(事实上并不需要特殊维护)
- v v v:当前区间所有数之和
-
s
s
s:历史所有
v
v
v之和
我们记录标记 ( a , b , c , d ) (a,b,c,d) (a,b,c,d),表示标记生效后: - v ′ = a ⋅ v + b ⋅ l v'=a\cdot v+b\cdot l v′=a⋅v+b⋅l
-
s
′
=
c
⋅
v
+
d
⋅
l
+
s
s'=c\cdot v+d\cdot l+s
s′=c⋅v+d⋅l+s
大概就是下面这个意思。
( 1 a b 0 c d 0 0 1 ) × ( s v l ) \begin{pmatrix} 1 & a & b\\ 0 & c & d\\ 0& 0 & 1 \end{pmatrix} \times \begin{pmatrix} s \\ v \\ l \end{pmatrix} ⎝⎛100ac0bd1⎠⎞×⎝⎛svl⎠⎞
标记的合并就是两个标记矩阵相乘。
我们加入一个数字就是在它作为最小值的部分打一个 ( 0 , 0 , 0 , a i ) (0,0,0,a_i) (0,0,0,ai),再在 [ 1 , i ] [1,i] [1,i]打一个 ( 1 , 0 , 1 , 0 ) (1,0,1,0) (1,0,1,0)即可。
复杂度 O ( n log n ) O(n\log n) O(nlogn),然后带一个大常数吧,并不用实现两个矩阵相乘,只需要将对应系数搞出来就行了。
莫队做法显然好理解很多。
设一个点作为最小值往左右分别能扩展到
L
i
,
R
i
L_i,R_i
Li,Ri。(其中要一边是小于,一边是小于等于)
考虑如果我们当前已经知道了
[
l
,
r
]
[l,r]
[l,r]的答案,将它减去所有左端点在
l
l
l,右端点在
[
l
,
r
]
[l,r]
[l,r]的区间内的最小值即为
[
l
+
1
,
r
]
[l+1,r]
[l+1,r]的答案。
显然右端点在 [ i , R i ] [i,R_i] [i,Ri]的贡献为 a i a_i ai,在 [ R i + 1 , R R i + 1 ] [R_i+1,R_{R_i+1}] [Ri+1,RRi+1]的贡献为 a R i + 1 a_{R_i+1} aRi+1。
依次类推,知道某个 p p p的 R p + 1 > r R_p+1>r Rp+1>r,剩下 [ p , r ] [p,r] [p,r]的贡献就是 a p a_p ap
不妨定义 a n + 1 = − ∞ a_{n+1}=-\infty an+1=−∞,则每个点的下一个都是固定的,如果我们将 i i i向 R i + 1 R_i+1 Ri+1连边,边权为 a i × ( R i − i + 1 ) a_i\times (R_i-i+1) ai×(Ri−i+1),那么会形成一棵树形结构,转移时先求出 [ l , r ] [l,r] [l,r]中最小值的位置,将答案减去 d i s l − d i s p + a p × ( r − p + 1 ) dis_l-dis_p+a_p\times (r-p+1) disl−disp+ap×(r−p+1)即可。
其他转移类似。
复杂度
O
(
n
n
)
O(n\sqrt n)
O(nn)
事实证明根号算法比log算法快。
【参考代码】
线段树
#include<bits/stdc++.h>
#define pb push_back
#define fi first
#define se second
#define mkp make_pair
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int N=1e5+10;
namespace IO
{
int read()
{
int ret=0,f=1;char c=getchar();
while(!isdigit(c)) {if(c=='-')f=0;c=getchar();}
while(isdigit(c)) ret=ret*10+(c^48),c=getchar();
return f?ret:-ret;
}
void write(ll x){if(x<0)x=-x,putchar('-');if(x>9)write(x/10);putchar(x%10^48);}
void writeln(ll x){write(x);putchar('\n');}
}
using namespace IO;
namespace Segment
{
struct tag
{
ll a,b,c,d;
tag(ll _a=0,ll _b=0,ll _c=0,ll _d=0):a(_a),b(_b),c(_c),d(_d){}
friend tag operator * (const tag&x,const tag&y)
{return tag(y.a+x.a*y.c,y.b+x.a*y.d+x.b,x.c*y.c,x.c*y.d+x.d);}
};
struct Seg
{
#define ls (x<<1)
#define rs (x<<1|1)
tag t[N<<2];ll val[N<<2],sum[N<<2];
void uptag(int x,int l,int r,const tag&v)
{
sum[x]=sum[x]+v.a*val[x]+v.b*(r-l+1);
val[x]=v.c*val[x]+v.d*(r-l+1);
t[x]=v*t[x];//wrong because t[x]=t[x]*v,the maxtrix mult
}
void pushdown(int x,int l,int r)
{
int mid=(l+r)>>1;
uptag(ls,l,mid,t[x]);uptag(rs,mid+1,r,t[x]);
t[x]=tag(0,0,1,0);
}
void pushup(int x){val[x]=val[ls]+val[rs];sum[x]=sum[ls]+sum[rs];}
void update(int x,int l,int r,int L,int R,const tag&v)
{
if(L<=l && r<=R){uptag(x,l,r,v);return;}
pushdown(x,l,r);
int mid=(l+r)>>1;
if(L<=mid) update(ls,l,mid,L,R,v);
if(R>mid) update(rs,mid+1,r,L,R,v);
pushup(x);
}
ll query(int x,int l,int r,int L,int R)
{
if(L<=l && r<=R) return sum[x];
pushdown(x,l,r);
int mid=(l+r)>>1;ll res=0;
if(L<=mid) res+=query(ls,l,mid,L,R);
if(R>mid) res+=query(rs,mid+1,r,L,R);
return res;
}
#undef ls
#undef rs
}tr;
}
using namespace Segment;
namespace DreamLolita
{
int n,Q,top,a[N];
ll ans[N];
pii stk[N];
vector<pii>q[N];
void solution()
{
n=read();Q=read();
for(int i=1;i<=n;++i) a[i]=read();
for(int i=1,x,y;i<=Q;++i)
x=read(),y=read(),q[y].pb(mkp(x,i));
for(int i=1;i<=n;++i)
{
while(top && stk[top].fi>=a[i]) --top;
tr.update(1,1,n,stk[top].se+1,i,tag(0,0,0,a[i]));
tr.update(1,1,n,1,i,tag(1,0,1,0));
stk[++top]=mkp(a[i],i);
for(int j=0;j<(int)q[i].size();++j)
ans[q[i][j].se]=tr.query(1,1,n,q[i][j].fi,i);
}
for(int i=1;i<=Q;++i) writeln(ans[i]);
}
}
int main()
{
#ifdef Durant_Lee
freopen("BZOJ4540.in","r",stdin);
freopen("BZOJ4540.out","w",stdout);
#endif
DreamLolita::solution();
return 0;
}
莫队
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e5+10,inf=1e9,lim=308;
namespace IO
{
int read()
{
int ret=0,f=1;char c=getchar();
while(!isdigit(c)) {if(c=='-')f=0;c=getchar();}
while(isdigit(c)) ret=ret*10+(c^48),c=getchar();
return f?ret:-ret;
}
void write(ll x){if(x<0)x=-x,putchar('-');if(x>9)write(x/10);putchar(x%10^48);}
void writeln(ll x){write(x);putchar('\n');}
}
using namespace IO;
namespace DreamLolita
{
int n,Q,top;
int bl[N],stk[N],L[N],R[N],a[N];
int Log[N],fc[25],st[19][N];
ll res,ans[N],sl[N],sr[N];
struct Tquery
{
int l,r,id;
bool operator <(const Tquery&rhs)const{return bl[l]==bl[rhs.l]?r<rhs.r:bl[l]<bl[rhs.l];}
}q[N];
int calc(int x,int y){return a[x]<a[y]?x:y;}
int query(int x,int y)
{
if(x>y) swap(x,y);
int t=Log[y-x+1];
return calc(st[t][x],st[t][y-fc[t]+1]);
}
void init()
{
fc[0]=1;for(int i=1;i<20;++i) fc[i]=fc[i-1]<<1;
for(int i=2;i<N;++i) Log[i]=Log[i>>1]+1;
n=read();Q=read();
for(int i=1;i<=n;++i) a[i]=read(),bl[i]=(i-1)/lim+1,st[0][i]=i;
for(int j=1;j<=18;++j) for(int i=1;i+fc[j]-1<=n;++i)
st[j][i]=calc(st[j-1][i],st[j-1][i+fc[j-1]]);
for(int i=1;i<=n;++i)
{
while(top && a[stk[top]]>=a[i]) R[stk[top--]]=i;
stk[++top]=i;
}
while(top) R[stk[top--]]=n+1;
for(int i=n;i;--i)
{
while(top && a[stk[top]]>a[i]) L[stk[top--]]=i;
stk[++top]=i;
}
while(top) L[stk[top--]]=0;
for(int i=n;i;--i) sr[i]=sr[R[i]]+1ll*(R[i]-i)*a[i];
for(int i=1;i<=n;++i) sl[i]=sl[L[i]]+1ll*(i-L[i])*a[i];
}
void updatel(int l,int r,int v)
{
int p=query(l,r);
res+=(1ll*a[p]*(r-p+1)+sr[l]-sr[p])*v;//wrong because 1ll*v*a[p]...
}
void updater(int l,int r,int v)
{
int p=query(l,r);
res+=(1ll*a[p]*(p-l+1)+sl[r]-sl[p])*v;
}
void solve()
{
for(int i=1;i<=Q;++i) q[i].l=read(),q[i].r=read(),q[i].id=i;
sort(q+1,q+Q+1);res=a[1];
for(int l=1,r=1,i=1;i<=Q;++i)
{
for(;r<q[i].r;) ++r,updater(l,r,1);
for(;l>q[i].l;) --l,updatel(l,r,1);
for(;r>q[i].r;) updater(l,r,-1),--r;
for(;l<q[i].l;) updatel(l,r,-1),++l;
ans[q[i].id]=res;
}
for(int i=1;i<=Q;++i) writeln(ans[i]);
}
void solution(){init();solve();}
}
int main()
{
#ifdef Durant_Lee
freopen("BZOJ4540.in","r",stdin);
freopen("BZOJ4540.out","w",stdout);
#endif
DreamLolita::solution();
return 0;
}