这题分治。
对于一个区间[l,r]
我们可以分三种情况讨论:
(设左端点为a,右端点为b)
1:a,b都在[l,mid]
2:a,b都在[mid+1,r]
3:a,b跨过了mid,也就是a<=mid && b>mid
1,2都可以从下一层求出,所以我们只需要求出ans即可。
而3怎么求呢?
我们设mi[],ma[],sma[],smi[],s[]
i:
l<=i<=mid
mi[i]表示[i,mid]中的最小值
ma[i]表示[i,mid]中的最大值
smi[i]表示mi[i~mid]的和
sma[i]表示ma[i~mid]的和
s[i]表示∑ma[i]*mi[i](i<=mid)
mid+1<=i<=r
同理
我们可以枚举左端点a(mid->l)
然后找到右边([mid+1,r])中第一个大于ma[a]的位置u
第一个小于mi[a]的位置v
PS:(我们可以发现,在a向左移的时候,u,v的位置是单调递增的!!!)
我们分成三块来求:(设u<v)
1:[a,u-1]
它的最大值一定是mi[a],最小值一定是ma[a],然后再乘(u-mid)
2:[u,v-1]
它的最大值就是ma[a],而最小值的话不确定,所以我们可以用smi[v]-smi[u]来求和
3:[v,r]
它的最大值和最小值都不确定!!!
but,肯定不是mid左边的!!
因为u,v已经是比那些还要有的了!!!
所以我们就要用到s[]来求就可以了,为(s[r]-s[v])
好啦,分治完后输出答案即可。
上标:
#include<cstdio>
#include<algorithm>
#define N 500010
#define mo 1000000007
#define ll long long
using namespace std;
ll a[N],smi[N],sma[N],mi[N],ma[N],s[N],ans=0;
int n;
inline int read()
{
int x=0; char c=getchar();
while (c<'0' || c>'9') c=getchar();
while (c>='0' && c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar();
return x;
}
void solve(int l,int r)
{
if (l==r) {(ans+=a[l]*a[l])%=mo; return;}
int mid=l+r>>1;
solve(l,mid),solve(mid+1,r);
smi[mid]=sma[mid]=ma[mid]=s[mid]=0;
mi[mid]=1000000000;
for (int i=mid+1;i<=r;i++)
{
mi[i]=min(mi[i-1],a[i]),ma[i]=max(ma[i-1],a[i]);
smi[i]=(smi[i-1]+mi[i])%mo,sma[i]=(sma[i-1]+ma[i])%mo;
s[i]=(s[i-1]+mi[i]*ma[i])%mo;
}
ll mil=1000000000,mal=0,u=mid+1,v=mid+1;
for (int i=mid;i>=l;i--)
{
mil=min(mil,a[i]),mal=max(mal,a[i]);
while (mi[u]>mil && u<=r) u++;u--;
while (ma[v]<mal && v<=r) v++;v--;
if (u<v)
{
(ans+=(u-mid)*mal%mo*mil%mo)%=mo;
(ans+=(smi[v]-smi[u]+mo)%mo*mal%mo)%=mo;
(ans+=s[r]-s[v])%=mo;
}
else
{
(ans+=(v-mid)*mal%mo*mil%mo)%=mo;
(ans+=(sma[u]-sma[v]+mo)%mo*mil%mo)%=mo;
(ans+=s[r]-s[u])%=mo;
}
}
}
int main()
{
freopen("seq.in","r",stdin);
freopen("seq.out","w",stdout);
n=read();
for (int i=1;i<=n;i++) a[i]=read();
solve(1,n);
printf("%lld\n",ans);
return 0;
}