bzoj 3745 [Coci2015]Norma
题目的大意是求一个数列中任意一个区间的长度乘上区间最大值和最小值求和。
我们二分来做这个题,每次递归处理,然后我们只需统计所有跨过中点的区间的贡献即可。
我们枚举[l,mid]区间里的每一个数a[i] (l<=i<=mid),因为无论最大值和最小值随着区间扩大有单调性,所以我们维护两个指针,分别记录右面区间最远的能够到达的距离,满足[mid+1,p1] [mid+1,p2] 的区间最大/(最小值) 小于/(大于) 等于 [i,mid]的最大(最小)值。
然后我们分三块来统计,[i,min(p1,p2)]内所有的区间 , i到[max(p1,p2),r] 的所有区间 , i到[min(p1,p2),max(p1,p2)]所有区间,自己推一下式子就行啦。(中间少取了两次模,wa了好多次,感觉自己好蠢。。。。。。) ——by VANE
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int mod=1e9; const int N=5e5+5; void add(ll &x,ll y) {x=((x+y)%mod+mod)%mod;} ll getsum(int l,int r){return 1ll*(l+r)*(r-l+1)/2%mod;} ll f1[N],f2[N],f3[N],f4[N],a[N],n,ans,f5[N],f6[N]; ll mn[N],mx[N]; void solve(int l,int r) { if(l==r) {add(ans,a[l]*a[l]%mod);return;} int mid=l+r>>1; solve(l,mid);solve(mid+1,r); mn[mid]=mod;mx[mid]=-mod; f1[r+1]=0;f2[r+1]=0; f3[mid]=0;f4[mid]=0; f5[mid]=0;f6[mid]=0; int p1,p2; for(int i=mid+1;i<=r;++i) { mn[i]=min(mn[i-1],a[i]);mx[i]=max(mx[i-1],a[i]); f3[i]=f3[i-1]+i*mx[i]; f4[i]=f4[i-1]+i*mn[i]; f3[i]%=mod;f4[i]%=mod; f5[i]=f5[i-1]+mx[i];f5[i]%=mod; f6[i]=f6[i-1]+mn[i];f6[i]%=mod; } for(int i=r;i>=mid+1;--i) { f1[i]=f1[i+1]+mx[i]*mn[i]%mod*i%mod; f2[i]=f2[i+1]+mx[i]*mn[i]%mod; f1[i]%=mod;f2[i]%=mod; } p1=mid+1;p2=mid+1; ll mxl=-mod,mnl=mod; for(int i=mid;i>=l;--i) { mxl=max(mxl,a[i]); mnl=min(mnl,a[i]); while(p2<=r&&mn[p2]>=mnl) p2++; while(p1<=r&&mx[p1]<=mxl) p1++; p1--;p2--; add(ans,getsum(mid+1-i+1,min(p1,p2)-i+1)*mxl%mod*mnl%mod); add(ans,f1[max(p1,p2)+1]-f2[max(p1,p2)+1]*(i-1)%mod); if(p1<=p2) add(ans,mnl*(f3[p2]-f3[p1]-1ll*(i-1)*(f5[p2]-f5[p1])%mod)); else add(ans,mxl*(f4[p1]-f4[p2]-1ll*(i-1)*(f6[p1]-f6[p2])%mod)); } } int main() { cin>>n; for(int i=1;i<=n;++i) scanf("%lld",a+i); solve(1,n); printf("%lld\n",ans); }
生命中真正重要的不是你遭遇了什么,而是你记住了哪些事,又是如何铭记的。