bzoj 3745: [Coci2015]Norma

Description

题面

Solution

考虑分治:
我们要统计跨越 \(mid\) 的区间的贡献
分最大值和最小值所在位置进行讨论:

设左边枚举到了 \(i\),左边 \([i,mid]\) 的最大值为 \(mx\),最小值为 \(mn\)
1.最大值最小值都在左边:\(\sum_{j=mid+1}^{p}mx*mn*(j-i+1)\),可以用等差数列直接算出
2.最大/小值有一个在左边 \(\sum_{j=p}^{q}mx*Mx[j]*(j-i+1)\) 我们可以拆成 \(\sum_{j=p}^{q}mx*Mx[j]*j-\sum_{j=1}^{q}mx*Mx[j]*(i-1)\)
3.最大值最小值都在右边:同理,拆除 \(j\)\(i-1\) 这两项
分别维护前缀和即可

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=500005,mod=1e9;
int n,a[N],ans=0,sm[N],sn[N],smi[N],sni[N],s[N],Mn[N],Mx[N],sum[N];
inline void solve(int l,int r){
	if(l==r){ans=(ans+1ll*a[l]*a[l])%mod;return ;}
	int mid=(l+r)>>1;
	solve(l,mid);solve(mid+1,r);
	int mx=0,mn=mod,j=mid,k=mid;
	s[mid]=sm[mid]=sn[mid]=smi[mid]=sni[mid]=Mx[mid]=0;Mn[mid]=mod;
	for(int i=mid+1;i<=r;i++){
		mx=max(mx,a[i]);mn=min(mn,a[i]);Mx[i]=mx;Mn[i]=mn;
		sm[i]=(sm[i-1]+mx)%mod;sn[i]=(sn[i-1]+mn)%mod;
		smi[i]=(smi[i-1]+1ll*mx*i)%mod;sni[i]=(sni[i-1]+1ll*mn*i)%mod;
		s[i]=(s[i-1]+1ll*mx*mn%mod*i)%mod;sum[i]=(sum[i-1]+1ll*mx*mn)%mod;
	}
	mx=0;mn=mod;
	for(int i=mid;i>=l;i--){
		mx=max(mx,a[i]);mn=min(mn,a[i]);
		while(j<r && Mn[j+1]>=mn)j++;
		while(k<r && Mx[k+1]<=mx)k++;
		int p=min(j,k),q=max(j,k);
		ans=(ans+1ll*mx*mn%mod*((1ll*(mid-2*i+p+3)*(p-mid)>>1)%mod))%mod;
		ans=(1ll*ans+s[r]-s[q]-1ll*(sum[r]-sum[q])*(i-1))%mod;
		if(j<k)ans=(ans+1ll*mx*(1ll*sni[k]-sni[j]-1ll*(i-1)*(sn[k]-sn[j])%mod))%mod;
		else ans=(ans+1ll*mn*(1ll*smi[j]-smi[k]-1ll*(i-1)*(sm[j]-sm[k])%mod))%mod;
	}
}
int main(){
  freopen("pp.in","r",stdin);
  freopen("pp.out","w",stdout);
  scanf("%d",&n);
  for(int i=1;i<=n;i++)scanf("%d",&a[i]);
  solve(1,n);
  if(ans<0)ans+=mod;
  cout<<ans<<endl;
  return 0;
}

posted @ 2018-04-13 19:50  PIPIBoss  阅读(79)  评论(0编辑  收藏  举报