义乌集训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;
}