把博客园图标替换成自己的图标
把博客园图标替换成自己的图标end

2019.7.8 义乌模拟赛 T4 D

很妙的题目,考试的时候没做出来我果然是菜。
考虑对这个东西分治。
对于两边的分治下去即可。
然后对于跨越区间中点的特别处理。
对于左边处理出\(Sum_i\)表示\([i,m]\)内的最大前缀长度,\(Maxn_i\)表示\([i,m]\)内的最大子段和。
右边同理,那么对于一对\((i,j)\)答案就是\(\max(\max(Maxn_i,Maxn_j),Sum_i+Sum_j)\)
然后因为这道题很特殊所以满足左边\(Sum_i>Sum_{i+1}\)\(Maxn_i>Maxn_{i+1}\),右边同理。
那么我们枚举左边是哪个点然后右边维护指针指到最右边的小于\(Maxn_i\)的点然后二分找到答案即可。
再用右边往回处理一遍。
时间复杂度是\(O(nlog^2n)\)的但是快得惊奇,\(10^5\)的点才跑了\(33ms\)
code:

#include<bits/stdc++.h>
#include<set>
#define I inline
#define re register
#define Me(x,y) memset(x,y,sizeof(x))
#define N 100000
#define M 100000
#define W 25
#define db double
#define min(a,b) ((a)<(b)?(a):(b))
#define max(a,b) ((a)>(b)?(a) :(b))
#define it iterator
#define Gc() getchar()
#define ll long long
#define ull unsigned ll
using namespace std;
int n,m,A[N+5];ull ans,Q[N+5];ll now,Sum[N+5],Maxn[N+5];
I void solve(int l,int r){
	if(l==r) return (void)(ans+=A[l]);re int m=l+r>>1,i,rs,lx,rx,mid;solve(l,m);solve(m+1,r);
	for(Sum[m]=Maxn[m]=A[m],now=max(A[m],0),i=m-1;i>=l;i--)Sum[i]=Sum[i+1]+A[i],now+=A[i],Maxn[i]=max(Maxn[i+1],now),now=max(now,0);
	for(Q[m]=Sum[m],i=m-1;i>=l;i--) Sum[i]=max(Sum[i+1],Sum[i]),Q[i]=Q[i+1]+Sum[i];
	for(Sum[m+1]=Maxn[m+1]=A[m+1],now=max(A[m+1],0),i=m+2;i<=r;i++)Sum[i]=Sum[i-1]+A[i],now+=A[i],Maxn[i]=max(Maxn[i-1],now),now=max(now,0);
	for(Q[m+1]=Sum[m+1],i=m+2;i<=r;i++) Sum[i]=max(Sum[i-1],Sum[i]),Q[i]=Q[i-1]+Sum[i];
	Q[m]=0;for(rs=m+1,i=m;i>=l;i--){
		while(rs<=r&&Maxn[rs]<=Maxn[i]) rs++;lx=m;rx=rs;while(lx+1<rx) mid=lx+rx>>1,(Sum[mid]<Maxn[i]-Sum[i]?lx:rx)=mid;
		ans+=Q[rs-1]-Q[lx]+(rs-1-lx)*Sum[i]+Maxn[i]*(lx-m);//printf("%d %d %d %lld\n",i,l,r,Q[rs-1]-Q[lx]+(rs-1-lx)*Sum[i]+Maxn[i]*(lx-m));
	}Q[m]=Sum[m];
	Q[m+1]=0;for(rs=m,i=m+1;i<=r;i++){
		while(rs>=l&&Maxn[rs]<Maxn[i]) rs--;lx=rs;rx=m+1;while(lx+1<rx) mid=lx+rx>>1,(Sum[mid]>Maxn[i]-Sum[i]?lx:rx)=mid;
		ans+=Q[rs+1]-Q[rx]+(rx-1-rs)*Sum[i]+Maxn[i]*(m-lx);//printf("%d %d %d %lld\n",i,l,r,Q[rs-1]-Q[rx]+(rx+1-rs)*Sum[i]+Maxn[i]*(m-lx));
	}
}
int main(){
	freopen("d.in","r",stdin);freopen("d.out","w",stdout);
	re int i,j;scanf("%d",&n);for(i=1;i<=n;i++) scanf("%d",&A[i]);solve(1,n);printf("%llu\n",ans);
}
posted @ 2021-07-09 06:34  275307894a  阅读(52)  评论(1编辑  收藏  举报
浏览器标题切换
浏览器标题切换end