义乌集训7.8 contest 1题解

2021.7.8D

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

Description:

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

​ 求出所有 \(1\le l\le r\le 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<<'\n';*/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]<<'\n';*/}
	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]<<'\n';*/}
	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<<'\n';
}
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;
}
posted @ 2021-07-08 19:36  OdtreePrince  阅读(132)  评论(1编辑  收藏  举报