bzoj 3745: [Coci2015]Norma

考虑分治,对于区间[l,r],计算左端点在[l,mid],右端点在[mid,r]的区间对答案的影响

然后自己推公式。

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define rep(j,k,l) for (LL j=k;j<=l;++j)
#define red(j,k,l) for (LL j=k;j>=l;--j)
#define max(a,b) (((a)>(b))?(a):(b))
#define min(a,b) (((a)<(b))?(a):(b))
#define LL long long
#define mod 1000000000
#define N 500005

using namespace std;
LL a[N],n,ans,mx[N],mn[N],sx[N],sn[N],p[N];

void solve(LL l,LL r){
	
	if (l>r) return;
	if (l==r){ ans=(ans+a[l]*a[r])%mod; return; }
	LL mid=l+r>>1;
	solve(l,mid-1);solve(mid+1,r);
	
	LL Mx,Mn,nx,nm;
	mx[mid]=a[mid];rep(i,mid+1,r) mx[i]=max(a[i],mx[i-1]);red(i,mid-1,l) mx[i]=max(a[i],mx[i+1]);
	mn[mid]=a[mid];rep(i,mid+1,r) mn[i]=min(a[i],mn[i-1]);red(i,mid-1,l) mn[i]=min(a[i],mn[i+1]);
	sx[mid]=a[mid];rep(i,mid+1,r) sx[i]=(sx[i-1]+mx[i])%mod;red(i,mid-1,l) sx[i]=(sx[i+1]+mx[i])%mod;
	sn[mid]=a[mid];rep(i,mid+1,r) sn[i]=(sn[i-1]+mn[i])%mod;red(i,mid-1,l) sn[i]=(sn[i+1]+mn[i])%mod;
	p[mid]=a[mid]*a[mid]%mod;rep(i,mid+1,r) p[i]=(p[i-1]+mx[i]*mn[i])%mod;red(i,mid-1,l) p[i]=(p[i+1]+mx[i]*mn[i])%mod;
	
	Mx=mid,Mn=mid,nx=a[mid],nm=a[mid];
	red(i,mid,l){
		
		LL A=0;nx=max(nx,a[i]);nm=min(nm,a[i]);
		while (Mx<r&&mx[Mx+1]<=nx) Mx++;while (Mn<r&&mn[Mn+1]>=nm) Mn++;
		if (Mx==Mn) A=(nx*nm%mod*(Mx-mid+1)%mod+p[r]-p[Mx]+mod)%mod;
		else if (Mx<Mn) A=(nx*nm%mod*(Mx-mid+1)+(sx[Mn]-sx[Mx]+mod)*nm+p[r]-p[Mn]+mod)%mod; 
		else A=(nx*nm%mod*(Mn-mid+1)+(sn[Mx]-sn[Mn]+mod)*nx+p[r]-p[Mx]+mod)%mod;
		//printf("%lld %lld\n",Mx,Mn);
		ans=(ans+(1-i)*A%mod+mod)%mod;
		//printf("-!-%lld\n",ans);
		//printf("----%lld %lld %lld %lld\n",l,r,ans,A);
		
	}
	
	Mx=mid,Mn=mid,nx=a[mid],nm=a[mid];
	rep(i,mid,r){
		
		LL A=0;nx=max(nx,a[i]);nm=min(nm,a[i]);
		while (Mx>l&&mx[Mx-1]<=nx) Mx--;while (Mn>l&&mn[Mn-1]>=nm) Mn--;
		if (Mx==Mn) A=(nx*nm%mod*(mid-Mx+1)%mod+p[l]-p[Mx]+mod)%mod;
		else if (Mx>Mn) A=(nx*nm%mod*(mid-Mx+1)+(sx[Mn]-sx[Mx]+mod)*nm+p[l]-p[Mn]+mod)%mod; 
		else A=(nx*nm%mod*(mid-Mn+1)+(sn[Mx]-sn[Mn]+mod)*nx+p[l]-p[Mx]+mod)%mod;
		ans=(ans+i*A%mod)%mod;
		//printf("-*-%lld\n",ans);
		//printf("----%lld %lld %lld %lld\n",l,r,ans,A);
		
	}
	
}

int main(){
	
	//freopen("1.in","r",stdin);
	//freopen("1.out","w",stdout);
	scanf("%lld",&n);
	rep(i,1,n) scanf("%lld",a+i);
	solve(1,n);
	printf("%lld\n",ans);
	system("pause");
	
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值