题目链接
求:
∑ i = 1 n ∑ j = i n ( j − i + 1 ) m i n ( a i , a i + 1 , … a j ) m a x ( a i , a i + 1 , … a j ) \sum_{i=1}^{n}\sum_{j=i}^{n} (j-i+1)min(a_i,a_{i+1}, \dots a_j)\ max(a_i,a_{i+1}, \dots a_j) i=1∑nj=i∑n(j−i+1)min(ai,ai+1,…aj) max(ai,ai+1,…aj)
经典的序列分治(CDQ分治)题 , 很有思考价值
算法主题自然是分治
把区间分成两半后 , 只考虑跨过中点的区间
我们需要一种能很好求出 m i n , m a x min,max min,max的方法
不妨先把情况分一下类:
显然其实只有 min 和 max 是不是在同一侧的两种本质不同的情况 , 因为 min 和 max 是等价处理的
我们先考虑枚举一个左端点(或右端点) i i i
假设 min和max 在同一侧, 这个就很好办了 , 不管它在左在右 , 我们都可以维护一个单调指针来得到当前这一边的 min 和 max
由于不能和另一边产生冲突 , 另一边也要维护两个单调指针 , 这样就能找出使得 min max 都在同一侧的区间了 , 直接等差数列求和公式即可
而对于不在同一侧的 , 我们先写一下式子:
这里只讨论 min 在左而 max在右的情况 , 另一种一样的做法
a n s [ i ] = ∑ j = m i d + 1 r ( j − i + 1 ) ∗ m i n ( a [ i ] ∼ a [ m i d ] ) ∗ m a x ( a [ m i d + 1 ] ∼ a [ j ] ) ans[i]=\sum_{j=mid+1}^r (j-i+1) * min(a[i]\sim a[mid]) *max(a[mid+1]\sim a[j]) ans[i]=j=mid+1∑r(j−i+1)∗min(a[i]∼a[mid])∗max(a[mid+1]∼a[j])
我们可以通过单调指针得出 和 i i i 有关的min , 记为 M i n i Min_i Mini ,而后面的记为 M a x j Max_j Maxj
a n s [ i ] = M i n i ∑ j = m i d + 1 r ( j + 1 ) ∗ M a x j − i ∗ M a x j ans[i]=Min_i \sum_{j=mid+1}^{r} (j+1)*Max_j-i*Max_j ans[i]=Minij=mid+1∑r(j+1)∗Maxj−i∗Maxj
对于每一个 i i i合法的该种情况的区间一定是单调且连续的
于是维护一下 ( j + 1 ) ∗ M a x j (j+1)*Max_j (j+1)∗Maxj和 M a x j Max_j Maxj 的后缀和就行了
代码:
#include
#include
#include
#include
#include
#include
#include
using namespace std;
inline int read()
{
int x=0;char ch=getchar();int t=1;
for(;ch<'0'||ch>'9';ch=getchar()) if(ch=='-') t=-1;
for(;ch>='0'&&ch<='9';ch=getchar()) x=(x<<1)+(x<<3)+(ch-48);
return x*t;
}
typedef long long ll;
const int N=5e5+10;
const int mod=1e9;
int n,a[N],maxq[N],maxn[N],maxs[N],minn[N],minq[N],mins[N];
inline void upd(int &x,int y){x+=y;if(x>=mod) x-=mod;}
int ans=0;
inline ll SUM(int l,int r,int num){return 1ll*(l+r)*num/2%mod;}
void Divide(int l,int r)
{
if(l==r) return upd(ans,1ll*a[l]*a[l]%mod);
register int mid=l+r>>1;
Divide(l,mid);Divide(mid+1,r);
register int p=mid+1,q=mid+1;
register int Min=a[mid],Max=a[mid];
maxn[mid+1]=minn[mid+1]=a[mid+1];
for(register int i=mid+2;i<=r;++i) maxn[i]=max(maxn[i-1],a[i]),minn[i]=min(minn[i-1],a[i]);
maxq[r+1]=maxs[r+1]=minq[r+1]=mins[r+1]=0;
for(register int i=r;i>mid;--i) {
maxq[i]=mod-maxn[i];maxs[i]=1ll*(i+1)*maxn[i]%mod;
upd(maxq[i],maxq[i+1]);upd(maxs[i],maxs[i+1]);
minq[i]=mod-minn[i];mins[i]=1ll*(i+1)*minn[i]%mod;
upd(minq[i],minq[i+1]);upd(mins[i],mins[i+1]);
}
for(register int i=mid;i>=l;--i){
Min=min(Min,a[i]);Max=max(Max,a[i]);
while(p<=r&&a[p]<=Max) ++p;
while(q<=r&&a[q]>=Min) ++q;
register int pos=min(p,q)-1;
register ll D=1ll*Max*Min%mod;
upd(ans,D*SUM(mid-i+2,pos-i+1,pos-mid)%mod);
if(p<q) {
register ll S=1ll*Min*(((maxs[p]-maxs[q]+mod)%mod+1ll*i*(maxq[p]-maxq[q]+mod)%mod)%mod)%mod;
upd(ans,S);
}
else if(q<p){
register ll S=1ll*Max*(((mins[q]-mins[p]+mod)%mod+1ll*i*(minq[q]-minq[p]+mod)%mod)%mod)%mod;
upd(ans,S);
}
}
Min=a[mid+1],Max=a[mid+1];p=q=mid;
for(register int i=mid+1;i<=r;++i){
Min=min(Min,a[i]);Max=max(Max,a[i]);
while(p>=l&&a[p]<Max) --p;
while(q>=l&&a[q]>Min) --q;
register int pos=max(p,q)+1;
register ll D=1ll*Max*Min%mod;
upd(ans,D*SUM(i-mid+1,i-pos+1,mid+1-pos)%mod);
}
return;
}
int main()
{
n=read();
for(register int i=1;i<=n;++i) a[i]=read();
Divide(1,n);
printf("%d\n",ans);
}