——别撕时政材料啊,我明年补考还要用的。
——明年就有新材料了。
突然发现自己还不会这种分治算出所有子区间权值的套路,随机钦定了一道来练练。
对于一段中点为$mid$的区间$[l,r]$,它的贡献是经过$mid$的子区间权值加上$[l,mid]$和$[mid+1,r]$的贡献,然后就能分治下去了。
这样只需要考虑怎么算出经过某个点的区间的权值和就可以了。
我的脑回路不太正常,搞了一个无脑暴力的做法:
记$mi_i$为$i$到$mid$所有数的最小值,$ma_i$为最大值,$len_i$为$i$到$mid$的长度。
枚举右端点,考虑左端点的影响。对于$j\in[l,mid],i\in[mid+1,r]$,大力分类讨论$[j,i]$的贡献:
- $case1:mi_j< mi_i\land ma_i>ma_i:mi_jma_j(len_j+len_i)$
- $case2:mi_j<mi_i\land ma_j\le ma_i:mi_jma_i(len_j+len_i)$
- $case3:mi_j\ge mi_i\land ma_j>ma_i:mi_ima_j(len_j+len_i)$
- $case4:mi_j\ge mi_i\land ma_j\le ma_i:mi_ima_i(len_j+len_i)$
显然$ma$和$mi$是单调的,而且一个左端点的变化情况只有可能是$case1\rightarrow case2 \rightarrow case4$或$case1\rightarrow case3\rightarrow case4$。所以可以用一个指针维护左端点情况,在随着右端点移动向左跑;再对$case2,3$开两个队列判断是否可以进入$case4$,对每种$case$开两个变量随便搞一搞就行了。复杂度还是$O(n\log n)$。
最棒的是这个方法无论是代码难度、美观度还是常数都被正解踩爆了呢
代码:
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <queue>
#define maxn 500005
#define inf 0x3f3f3f3f
const int mod = 1e9;
using namespace std;
inline int read(){
int x=0,y=0;
char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')y=1;ch=getchar();}
while(ch>='0'&&ch<='9')x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
return y?-x:x;
}
int a[maxn],mi[maxn],ma[maxn],mul[maxn],ans;
queue<int>min_q,max_q;
#define clear(q) while(!q.empty())q.pop()
inline int mix(int x,int y){return x+y>=mod?x+y-mod:x+y;}
void solve(int l,int r){
if(l==r){ans=mix(ans,1ll*a[l]*a[l]%mod);return;}
clear(min_q);
clear(max_q);
int mid=l+r>>1,j=mid,min_max_sum,min_max_cnt,min_sum=0,min_cnt=0,max_sum=0,max_cnt=0,sum=0,cnt=0,Mi=inf,Ma=0;
mi[mid]=ma[mid]=a[mid],min_max_sum=min_max_cnt=mul[mid]=1ll*a[mid]*a[mid]%mod;
for(register int i=mid-1;i>=l;--i)mi[i]=min(mi[i+1],a[i]),ma[i]=max(ma[i+1],a[i]),mul[i]=1ll*mi[i]*ma[i]%mod,min_max_sum=mix(min_max_sum,1ll*mul[i]*(mid-i+1)%mod),min_max_cnt=mix(min_max_cnt,mul[i]);
for(register int i=mid+1;i<=r;++i){
Mi=min(Mi,a[i]),Ma=max(Ma,a[i]);
while(j>=l){
if(mi[j]<Mi&&ma[j]>Ma)break;
min_max_sum=mix(min_max_sum,mod-1ll*mul[j]*(mid-j+1)%mod);
min_max_cnt=mix(min_max_cnt,mod-mul[j]);
if(mi[j]>=Mi&&ma[j]<=Ma)sum=mix(sum,mid-j+1),++cnt;
else if(mi[j]>=Mi)max_sum=mix(max_sum,1ll*ma[j]*(mid-j+1)%mod),max_cnt=mix(max_cnt,ma[j]),max_q.push(j);
else min_sum=mix(min_sum,1ll*mi[j]*(mid-j+1)%mod),min_cnt=mix(min_cnt,mi[j]),min_q.push(j);
--j;
}
while(!min_q.empty()){
int k=min_q.front();
if(mi[k]<Mi)break;
min_sum=mix(min_sum,mod-1ll*mi[k]*(mid-k+1)%mod),min_cnt=mix(min_cnt,mod-mi[k]);
sum=mix(sum,mid-k+1),++cnt;
min_q.pop();
}
while(!max_q.empty()){
int k=max_q.front();
if(ma[k]>Ma)break;
max_sum=mix(max_sum,mod-1ll*ma[k]*(mid-k+1)%mod),max_cnt=mix(max_cnt,mod-ma[k]);
sum=mix(sum,mid-k+1),++cnt;
max_q.pop();
}
ans=mix(ans,mix(mix(mix(min_max_sum,1ll*min_max_cnt*(i-mid)%mod),mix(1ll*max_sum*Mi%mod,1ll*max_cnt*Mi%mod*(i-mid)%mod)),mix(mix(1ll*min_sum*Ma%mod,1ll*min_cnt*Ma%mod*(i-mid)%mod),mix(1ll*sum*Mi%mod*Ma%mod,1ll*cnt*Mi%mod*Ma%mod*(i-mid)%mod))));
}
solve(l,mid),solve(mid+1,r);
}
int main(){
int n=read();
for(register int i=1;i<=n;++i)a[i]=read();
solve(1,n);
printf("%d\n",ans);
}