义乌集训7.8 contest 1题解

2021.7.8D

​ 鉴于前3道题比较简单,就不写题解了,主要写一写T4的题解。

Description:

​ 给定一个序列 (a),定义一个区间 ([l,r]) 的最大子段和为 ([l,r]) 的一段连续子区间的和,满足其为 ([l,r]) 所有连续子区间和中最大的一个。

​ 求出所有 (1le lle rle n) 的区间 ([l,r]) 的最大子段和的和,答案对 (2^{64}) 取模。

​ 具体来说,假设答案为 (x),你需要输出最小的非负整数 (y),满足 (x)(y)(2^{64}) 同余。

Input:

​ 第一行一个数 (n)

​ 第二行 (n) 个数,第 (i) 个表示 (a_i)

Output:

​ 一行一个数表示答案。

Sample1 Input:

5
1 -1 1 -1 1

Sample1 Output:

11

Sample2 Input:

5
1 -2 3 -4 5

Sample2 Output:

39

Hint:

本题采用子任务评测。

对于 (30\%) 的数据,(1 leq n leq 10^{3})

对于另外 (20\%) 的数据,(a_i geq 0)

对于 (100\%) 的数据,(1 leq n leq 10^{5},-10^{9} leq a_i leq 10^{9})

题目分析:

​ 前两个数据点的分数比较好拿。(只要别脑抽写个高精一般就不会死,自然溢出是个好东西

​ 正解:考虑分治。对于一段区间([l,r])内的所有答案,即 (l leq i leq j leq r) 的所有([i,j])内的最大子段和。我们找到中点(mid),答案便可以拆成([l,mid])([mid+1,r])以及 (l leq i leq mid < j leq r) 的贡献之和。前两个贡献递归处理,我们只需考虑第三种贡献的求法。

​ 对于一对([i,j]),它可能的最大子段和分三种情况:([i,mid])的最大子段和,([mid+1,j])的最大子段和,跨过(mid)的最大子段和(即一段以(mid)为右端点的最大后缀加上以(mid)为左端点的最大前缀)

​ 那么如果我们固定了 (i),那么对于({forall } mid+1 leq j leq r),我们需要比较这三种情况的大小取最大值。

​ 考虑分类讨论——

​ 我们设以(mid)为右端点且左端点大于 (i) 的最大后缀为(A_i),以(mid)为左端点且右端点小于 (i) 的最大前缀为(B_i)([i,mid])的最大子段和为(C_i)([mid+1,i])的最大子段和为(D_i)。我们可以简单地发现(A,B,C,D)都有显然的单调性。

​ 1‘ (C_i geq D_j)时,对于固定的 (i) 我们可以通过(two-points)或二分迅速的找到对应的 (j) 的上界,设其为 (k) ,那么我们只需比较 (mid+1 leq j leq k)(j)({A_i}+{B_j})(C_i) 的大小。

​ 1‘’ (A_i+B_j leq C_i),即 (C_i-A_I geq B_j) ,那么我们可以在 ([mid+1,k]) 中二分出一个分界线 (p),满足 (mid+1 leq j leq p)(C_i-A_I geq B_j),用前缀和之类的东西计算其贡献。

​ 2'' (A_i+B_j >C_i),也就是 (p+1 leq j leq k) ,计算其贡献。

​ 2’ (C_i < D_j)时,我们按照同样的方法计算。

​ 于是这道题就愉快地结束了。

代码如下(马蜂很丑,不喜勿喷)——

#include<bits/stdc++.h>
#define N 100005
#define LL long long
#define ULL unsigned long long
#define inf 2147483647
using namespace std;
int n;LL a[N],A[N],B[N],C[N],D[N],S[N];ULL ans;
inline void solve(int l,int r){
	if(l>r) return;if(l==r){ans+=a[r]-a[l-1];/*cout<<l<<' '<<r<<' '<<ans<<'
';*/return;}int mid=l+r>>1;solve(l,mid),solve(mid+1,r);B[mid]=A[mid+1]=-inf;LL res=0,Max=-inf;
	for(register int i=mid;i>=l;i--){A[i]=max(A[i+1],a[mid]-a[i-1]);if(res<0) res=0;res+=a[i]-a[i-1],Max=max(Max,res),C[i]=Max;/*cout<<i<<' '<<mid<<' '<<C[i]<<'
';*/}
	Max=-inf,res=0;for(register int i=mid+1;i<=r;i++){B[i]=max(B[i-1],a[i]-a[mid]);if(res<0) res=0;res+=a[i]-a[i-1],Max=max(Max,res),D[i]=Max;/*cout<<mid+1<<' '<<i<<' '<<D[i]<<'
';*/}
	int i=mid,j=mid+1;S[j-1]=0;while(i>=l){
		while(j<=r&&D[j]<=C[i]) S[j]=S[j-1]+B[j],j++;int L=mid+1,R=j-1,num=j;
		while(L<=R){int M=L+R>>1;if(C[i]-A[i]<=B[M]) R=M-1,num=M;else L=M+1;}
		ans+=1ull*C[i]*(num-mid-1)+(ULL)S[j-1]-(ULL)S[num-1]+1ull*(j-num)*A[i],i--;
	}
	i=mid,j=mid+1,S[i+1]=0;while(j<=r){
		while(i>=l&&D[j]>C[i]) S[i]=S[i+1]+A[i],i--;int L=i+1,R=mid,num=i;
		while(L<=R){int M=L+R>>1;if(D[j]-B[j]<=A[M]) L=M+1,num=M;else R=M-1;}
		ans+=1ull*D[j]*(mid-num)+(ULL)S[i+1]-(ULL)S[num+1]+1ull*(num-i)*B[j],j++;
	}
//	cout<<l<<' '<<r<<' '<<mid<<' '<<ans<<'
';
}
inline int read(){
	int ret=0,f=1;char ch=getchar();while(!isdigit(ch)){if(ch=='-') f=-f;ch=getchar();}
	while(isdigit(ch)) ret=(ret<<1)+(ret<<3)+ch-'0',ch=getchar();return ret*f;
}
int main(){
	n=read();for(register int i=1;i<=n;i++) a[i]=read()+a[i-1];solve(1,n),cout<<ans;return 0;
}
原文地址:https://www.cnblogs.com/jiangxuancheng/p/14987843.html