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;
}