題目描述
你有一支由n名預備役士兵組成的部隊,士兵從1到n編號,要將他們拆分成若干特別行動隊調入戰場。出於默契考慮,同一支特別行動隊中隊員的編號應該連續,即爲形如(i,i+1,…,i+k)的序列。
編號爲i的士兵的初始戰鬥力爲xi,一支特別運動隊的初始戰鬥力x爲隊內士兵初始戰鬥力之和,即x=(xi)+(xi+1)+…+(xi+k)。
通過長期的觀察,你總結出一支特別行動隊的初始戰鬥力x將按如下經驗公式修正爲x’:x’=ax^2+bx+c,其中a,b,c是已知的係數(a<0)。
作爲部隊統帥,現在你要爲這支部隊進行編隊,使得所有特別行動隊修正後戰鬥力之和最大。試求出這個最大和。
例如,你有4名士兵,x1=2,x2=2,x3=3,x4=4。經驗公式中的參數爲a=-1,b=10,c=-20。此時,最佳方案是將士兵組成3個特別行動隊:第一隊包含士兵1和士兵2,第二隊包含士兵3,第三隊包含士兵4。特別行動隊的初始戰鬥力分別爲4,3,4,修正後的戰鬥力分別爲4,1,4。修正後的戰鬥力和爲9,沒有其它方案能使修正後的戰鬥力和更大。
輸入格式
輸入由三行組成。第一行包含一個整數n,表示士兵的總數。第二行包含三個整數a,b,c,經驗公式中各項的係數。第三行包含n個用空格分隔的整數x1,x2,…,xn,分別表示編號爲1,2,…,n的士兵的初始戰鬥力。
輸出格式
輸出一個整數,表示所有特別行動隊修正戰鬥力之和的最大值。
樣例數據
樣例輸入
4
-1 10 -20
2 2 3 4
樣例輸出
9
數據範圍
20%的數據中,n<=1000;
50%的數據中,n<=10000;
100%的數據中,1<=n<=1000000,-5<=a<=-1,|b|<=10000000,|c|<=10000000,1<=xi<=100。
題目分析
被這題坑慘了。
容易寫出動規方程
考慮斜率優化
化簡爲
令
因爲k單調遞增且爲正,維護下凸包
當然也可以令
但因爲a爲負,所以k單調遞減,維護上凸包
一定要看數據範圍啊,a爲負
源代碼
#include<algorithm>
#include<iostream>
#include<iomanip>
#include<cstring>
#include<cstdlib>
#include<vector>
#include<cstdio>
#include<cmath>
#include<queue>
using namespace std;
typedef long long LL;
inline const LL Get_Int() {
LL num=0,bj=1;
char x=getchar();
while(x<'0'||x>'9') {
if(x=='-')bj=-1;
x=getchar();
}
while(x>='0'&&x<='9') {
num=num*10+x-'0';
x=getchar();
}
return num*bj;
}
LL n,f[1000005],Cost[1000005],sum[1000005],Q[1000005],ans=0,Cut=0;
double Slope(int j,int k) {
return (double)(f[j]-f[k])/(k-j);
}
int main() {
cin>>n;
for(int i=1; i<=n; i++)Cost[i]=Get_Int();
for(int i=1; i<=n; i++)sum[i]=sum[i-1]+Get_Int();
for(int i=1; i<n; i++)ans+=(sum[i]-sum[i-1])*(n-i);
ans+=Cost[n];
int Left=1,Right=1;
Q[1]=n;
for(int i=n-1; i>=1; i--) {
while(Left<Right&&Slope(Q[Left],Q[Left+1])>=sum[i])Left++; //維護隊首(刪除非最優決策)
int Front=Q[Left];
f[i]=f[Front]+sum[i]*(Front-i)-Cost[i];
Cut=max(Cut,f[i]);
while(Left<Right&&Slope(Q[Right-1],Q[Right])<=Slope(Q[Right],i))Right--; //維護隊尾(維護上凸包性質)
Q[++Right]=i;
}
printf("%lld\n",ans-Cut);
return 0;
}