3745: [Coci2015]Norma

知道用分治以后,其实这题就不用再看题解了。

考虑i∈[l,mid],j∈[mid,r]时对答案的贡献。

记录从mid往左、往右时最小值、最大值的变化情况,将如图所示。

黄色表示最大值,蓝色表示最小值,然后绿色表示黄色和蓝色重合。。。

统计时,指针i从mid向左移动,j、k从mid向右移动,保证Min(mid...j)大于等于Min(i...mid)、Max(mid...k)≤Max(i...mid)。

对于每个i,均可分成三段统计,共下图两种情况。

对右边进行前缀和,S00、S01、S02表示蓝色、黄色、蓝黄相乘的和,S10[j]、S11[j]、S12[j]表示每个点(蓝色、黄色、蓝黄相乘)后乘上j-mid+1。具体见代码。

#include<cstdio>
#include<iostream>
#include<cmath>
#include<algorithm>
using namespace std;
#define rep(i,j,k) for(i=j;i<=k;++i)
#define per(i,j,k) for(i=j;i>=k;--i)
#define sqr(x) ((x)*(x))
#define G getchar()
#define LL long long
#define pll pair<LL,LL>
#define mkp make_pair
#define X first
#define Y second
#define N 500005
#define M 1000000000
LL n,a[N],ans,Max[N],Min[N],mmp[N],S00[N],S01[N],S02[N],S10[N],S11[N],S12[N];
int read(){
	int x=0;char ch=G;
	for(;ch<48||ch>57;ch=G);
	for(;ch>47&&ch<58;ch=G)x=x*10+ch-48;
	return x;
}
LL add(LL x,LL y){
	LL rtn=x+y;
	if(rtn>=M)rtn-=M;
	return rtn;
}
LL sub(LL x,LL y){
	LL rtn=x-y;
	if(rtn<0)rtn+=M;
	return rtn;
}
LL mul(LL x,LL y){
	return x*y%M;
}
LL cal(LL x,LL y){
	return ((x+y)*(x-y+1)>>1)%M;
}
void cdq(LL l,LL r){
	if(l>r)return;
	if(l==r){
		ans=add(ans,mul(a[l],a[l]));return;
	}
	LL mid=l+r>>1,i,j,k;
	Min[mid]=Max[mid]=S00[mid]=S01[mid]=S02[mid]=S10[mid]=S11[mid]=S12[mid]=a[mid];
	mmp[mid]=mul(a[mid],a[mid]);
	per(i,mid-1,l)
		mmp[i]=mul(Max[i]=max(Max[i+1],a[i]),Min[i]=min(Min[i+1],a[i]));
	rep(i,mid+1,r){
		mmp[i]=mul(Max[i]=max(Max[i-1],a[i]),Min[i]=min(Min[i-1],a[i]));
		S00[i]=add(S00[i-1],Min[i]);
		S01[i]=add(S01[i-1],Max[i]);
		S02[i]=add(S02[i-1],mmp[i]);
		S10[i]=add(S10[i-1],mul(i-mid+1,Min[i]));
		S11[i]=add(S11[i-1],mul(i-mid+1,Max[i]));
		S12[i]=add(S12[i-1],mul(i-mid+1,mmp[i]));
	}
	per(i,j=k=mid,l){
		while(j<r&&Min[j+1]>=Min[i])++j;
		while(k<r&&Max[k+1]<=Max[i])++k;
		if(j<k){
			ans=add(ans,mul(mmp[i],cal(j-i+1,mid-i+1)));
			ans=add(ans,mul(Max[i],add(sub(S10[k],S10[j]),mul(sub(S00[k],S00[j]),mid-i))));
			if(k<r)ans=add(ans,add(sub(S12[r],S12[k]),mul(sub(S02[r],S02[k]),mid-i)));
		}
		else if(j>k){
			ans=add(ans,mul(mmp[i],cal(k-i+1,mid-i+1)));
			ans=add(ans,mul(Min[i],add(sub(S11[j],S11[k]),mul(sub(S01[j],S01[k]),mid-i))));
			if(j<r)ans=add(ans,add(sub(S12[r],S12[j]),mul(sub(S02[r],S02[j]),mid-i)));
		}
		else{
			ans=add(ans,mul(mmp[i],cal(j-i+1,mid-i+1)));
			if(k<r)ans=add(ans,add(sub(S12[r],S12[k]),mul(sub(S02[r],S02[k]),mid-i)));
		}
	}
	cdq(l,mid-1);cdq(mid+1,r);
}
int main(){
	int i;
	n=read();rep(i,1,n)a[i]=read();
	cdq(1LL,n);printf("%lld\n",ans);
	return 0;
}

又把膜数打错了鈤。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值