Description
Input
输入文件名为seq.in。
首先输入n。
接下来输入n个数,描述序列 A。
Output
输出文件名为seq.out。
输出一行一个整数代表答案。
Sample Input
7
0 35 40 45 56 65 94
Sample Output
66636
Data Constraint
对于30%的数据,n<=5000
对于60%的数据,n<=50000
对于100%的数据,n<=500000,0<=A[i]<=10^9
Source / Author: 翁文涛
题解:
考虑分治,sol(l,r)为l~r的贡献
前面两个直接递归下去,重点是后面的,也就是两个指针分别在mid两边的情况。
我们先预处理出min[r] , max[r]表示mid+1 ~ r的最小值和最大值,sumin[r]表示mid+1 ~ r的最小值前缀和,sum[r]表示mid+1 ~ r的最大值*最小值的前缀和。
然后枚举左端点l,一边做一边记录l~mid的最小值和最大值min_l,max_l
对于当前的min_l,我们找到u,使得min[u]恰小于min_l
对于当前的max_l,我们找到v,使得max[v]恰大于max_l
(具体:随着l往左,min_l减小,要使min[u]小于min_l,只会往右
max同理
因此u,v两个指针维护即可
)
如图所示,我们假设u在v左边,这时候右端点r有三种情况。
(左闭右开)
- mid~u :最大值与最小值是min_l,max_l , r在这段区间的贡献总和为(u-mid-1)*max_l*min_l
- u~v :最小值不确定 (因为过了点u), 最大值是max_l。但我们知道这段区间的贡献为maxl*min[u] + maxl*min[u+1]+...+maxl*min[v-1]
- v~R :最大值和最小值都不确定(点u、v都过了)。但我们知道这段区间的贡献为min[v]*max[v]+min[v+1]*max[v+1]..+min[R]*max[R]
发现之前我们维护的数组sumin和sum数组有用场了(见上面)。对于第2种情况,提出maxl,就可以变成maxl*(sumin[v-1] - sumin[u-1]);
对于第3种,不和l~mid这段区间有关(我们固定了左端点),直接是sum[R]-sum[v-1];
v<u同理
自此问题迎刃而解。
O(log n *n)
#include<bits/stdc++.h>
#define N 500010
#define inf 2147483647
#define rint register ll
#define ll long long
#define point(a) multiset<a>::iterator
#define mod (ll)(1e9+7)
#define mem(a,b) memset(a,b,sizeof (a))
#define open(x) freopen(x".in","r",stdin);freopen(x".out","w",stdout);
using namespace std;
ll t,ans,n,i;
ll minn[N],maxx[N],sumin[N],sum[N],a[N],sumax[N];
ll sol(ll L,ll R)
{
if(L>R)return 0;
if(L==R)return a[L]*a[R];
ll ans=0;
ll mid=(L+R)/2;
maxx[mid]=0;
minn[mid]=inf;
sumin[mid]=sumax[mid]=sum[mid]=0;
//init()
for(ll r=mid+1;r<=R;r++)
{
minn[r] = min(minn[r-1] , a[r]);
maxx[r] = max(maxx[r-1] , a[r]);
(sum[r] = sum[r-1] + minn[r] *maxx[r]%mod)%=mod;
(sumin[r] = sumin[r-1]+minn[r])%=mod;
(sumax[r] = sumax[r-1]+maxx[r])%=mod;
}
ll u=mid+1,v=mid+1,max_l=-inf,min_l=inf;
for(ll l=mid;l>=L;--l)
{
min_l=min(min_l,a[l]);
max_l=max(max_l,a[l]);
while(u<=R && minn[u]>=min_l)++u;
while(v<=R && maxx[v]<=max_l)++v;
if(u<=v)
{
(ans+=min_l*max_l%mod*(u-mid-1)%mod)%=mod;
(ans+=max_l*(sumin[v-1] - sumin[u-1])%mod)%=mod;
(ans+=(sum[R] - sum[v-1])%mod)%=mod;
}else
{
(ans+=min_l*max_l%mod*(v-mid-1)%mod)%=mod;
(ans+=min_l*(sumax[u-1] - sumax[v-1])%mod)%=mod;
(ans+=(sum[R] - sum[u-1])%mod)%=mod;
}
}
return ((ans+sol(L,mid)%mod) +sol(mid+1,R))%mod;
}
void read(ll &x)
{
char ch=getchar();
x=0;
while(!isdigit(ch))ch=getchar();
while(isdigit(ch))x=(x<<1)+(x<<3)+ch-'0',ch=getchar();
return ;
}
void scan()
{
read(n);
for(i=1;i<=n;i++)read(a[i]);
}
int main()
{
open("seq");
scan();
ans=sol(1,n)%mod;
printf("%lld\n",ans);
return 0;
}