Description
给定N个数A[1..n]。有两种操作:
操作1:对于某对(i,j)(i
Input
第一行一个数N,N<=300000
接下来N个数表示A[1..n]
Output
两个数,分别表示只使用一次操作1和只使用一次操作2的两种情况下,sum(A[1..n])的和的最大值。
Sample Input
6
6 10 -7 2 5 -12
Sample Output
19
56
解释:
操作1取i=3,j=5;
操作2取i=2,j=6
HINT
Source
其实这道题是不难的….
写出状态转移方程,瞎瘠薄移一下就好了:
在上凸包上二分查找就行了
然鹅一开始没看到
,也就是必须得有更改
而且在二分的时候也出了问题
最后要判断右端点左端点,有三个可能的
的值
还是写太急了啊…..
#include<bits/stdc++.h>
using namespace std;
#define ll long long
ll n , a[301000] ;
ll s[301000] ,dp[301000];
ll q[301000] , head , tail;
bool flag;
ll read(){
ll sum = 0;char c = getchar();bool flag = true;
while( c < '0' || c > '9' ) {if(c == '-') flag = false;c = getchar();}
while( c >= '0' && c <= '9' ) sum = sum * 10 + c - 48 , c = getchar();
if(flag) return sum;
else return -sum;
}
double f(ll k , ll i){
return (s[i] - s[k] ) * 1.0 /(i - k) ;
}
int find(int x){
ll l = head + 1 , r = tail;
while( l + 1 < r ){
int mid = (l + r) >> 1;
if( f(q[mid] , q[mid - 1]) > x) l = mid;
else r = mid;
}
if( f(q[r] , q[r - 1]) > x ) return r;
else if(f(q[l],q[l - 1]) > x ) return l;
else return l - 1;
}
void work(){
q[1] = 0;head = 1;tail = 1;
ll ans = -100000000000000000LL;
for(int i = 2;i <= n;++i){
while(tail - head > 1 && f(q[tail - 1], q[tail]) > f(q[tail - 2],q[tail - 1])) q[--tail] = q[tail + 1];
int k = q[find(a[i])];
dp[i] = s[k] - a[i] * k + a[i] * i - s[i];
ans = max ( ans , dp[i] );
q[++tail] = i - 1;
}
printf("%lld\n",ans + s[n]);
q[1] = 0;head = 1;tail = 1;
for(int i = 1;i <= n;++i) s[i] = a[n - i + 1];
for(int i = 1;i <= n;++i) a[i] = s[i];
memset(s,0,sizeof(s));
for(int i = 1;i <= n;++i)
s[i] = s[i - 1] + a[i];
ans = -100000000000000000LL;
for(int i = 2;i <= n;++i){
while(tail - head > 1 && f(q[tail - 1], q[tail]) > f(q[tail - 2],q[tail - 1])) q[--tail] = q[tail + 1];
int k = q[find(a[i])];
dp[i] = s[k] - a[i] * k + a[i] * i - s[i];
ans = max ( ans , dp[i] );
q[++tail] = i - 1;
}
printf("%lld\n",ans+s[n]);
return;
}
int main(){
n = read();
for(int i = 1;i <= n;++i) a[i] = read();
for(int i = 1;i <= n;++i) s[i] = s[i - 1] + a[i];
work();
return 0;
}