[luogu P3628] [APIO2010]特別行動隊
[luogu P3628] [APIO2010]特別行動隊
題目描述
你有一支由 n 名預備役士兵組成的部隊,士兵從 1 到 n 編號,要將他們拆分 成若幹特別行動隊調入戰場。出於默契的考慮,同一支特別行動隊中隊員的編號 應該連續,即為形如(i, i + 1, ..., i + k)(i,i+1,...,i+k)的序列。 編號為 i 的士兵的初始戰鬥力為 xi ,一支特別行動隊的初始戰鬥力 x 為隊內 士兵初始戰鬥力之和,即 x = x_i + x_{i+1} + ... + x_{i+k}x=x?i??+x?i+1??+...+x?i+k??。
通過長期的觀察,你總結出一支特別行動隊的初始戰鬥力 x 將按如下經驗公 式修正為 $x‘:x‘ = ax2 + bx + c$,其中 a, b, c 是已知的系數(a < 0)。 作為部隊統帥,現在你要為這支部隊進行編隊,使得所有特別行動隊修正後 戰鬥力之和最大。試求出這個最大和。
例如,你有 4 名士兵, x_1 = 2, x_2 = 2, x_3 = 3, x_4 = 4x?1??=2,x?2??=2,x?3??=3,x?4??=4。經驗公式中的參數為 a = –1, b = 10, c = –20。此時,最佳方案是將士兵組成 3 個特別行動隊:第一隊包含士兵 1 和士兵 2,第二隊包含士兵 3,第三隊包含士兵 4。特別行動隊的初始戰鬥力分 別為 4, 3, 4,修正後的戰鬥力分別為 4, 1, 4。修正後的戰鬥力和為 9,沒有其它 方案能使修正後的戰鬥力和更大。
輸入輸出格式
輸入格式:
輸入由三行組成。第一行包含一個整數 n,表示士兵的總數。第二行包含三 個整數 a, b, c,經驗公式中各項的系數。第三行包含 n 個用空格分隔的整數 $x_1, x_2, …, x_n$,分別表示編號為 1, 2, …, n 的士兵的初始戰鬥力。
輸出格式:
輸出一個整數,表示所有特別行動隊修正後戰鬥力之和的最大值。
輸入輸出樣例
輸入樣例#1:4
-1 10 -20
2 2 3 4
輸出樣例#1:9
說明
20%的數據中,n ≤ 1000;
50%的數據中,n ≤ 10,000;
100%的數據中,1 ≤ n ≤ 1,000,000,–5 ≤ a ≤ –1,|b| ≤ 10,000,000,|c| ≤ 10,000,000,1 ≤ xi ≤ 100。
斜率DP pro 2。相比上一題,本蒟蒻感覺這題水多了,只是調了一下才A。
設f[i]為前i個人的戰鬥力最大值,s[i]為前i個人戰鬥力的和。易得:
f[i]=max{f[j]+a(s[i]-s[j])^2+b(s[i]-s[j])+c}(j<i)
=max{f[j]+as[i]^2-2as[i]s[j]+as[j]^2+bs[i]-bs[j]+c}
設X[i]=as[i]^2,Y[i]=bs[i],則原式
=max{f[j]+X[i]-2as[i]s[j]+X[j]+Y[i]-Y[j]+c}
設P[i]=X[i]+Y[i],Q[i]=X[i]-Y[i],則原式
=max{f[j]+P[i]+Q[i]-2as[i]s[j]+c}
則f[i]=?f[j]+P[i]+Q[i]-2as[i]s[j]+c
則在這個式子裏,y=f[j]+Q[j],k=2as[i],x=s[j],b=f[i]-P[i]-c,且y=kx+b。
由於是取max,所以是維護一個上凸包,所以維護一個斜率只降不升的單調隊列就可以了。
其中每個點的坐標為(xi,yi)=(s[i],f[i]+Q[i])(可以從最後那個x和y的表達式看出來)。
code:
1 %:pragma GCC optimize(2) 2 #include<bits/stdc++.h> 3 #define sqr(x) ((x)*(x)) 4 #define LL long long 5 using namespace std; 6 const int N=1000005; 7 const double inf=1e18; 8 int n,l,r; LL A,B,C,ratio,s[N],X[N],Y[N],P[N],Q[N],f[N]; 9 struct point { 10 LL x,y; 11 point() {} 12 point(LL _x,LL _y):x(_x),y(_y) {} 13 }st[N]; 14 inline int read() { 15 int x=0,f=1; char ch=getchar(); 16 while (ch<‘0‘||ch>‘9‘) f=(ch==‘-‘)?-1:1,ch=getchar(); 17 while (ch>=‘0‘&&ch<=‘9‘) x=x*10+ch-‘0‘,ch=getchar(); 18 return x*f; 19 } 20 double slope(point u,point v) { 21 return u.x==v.x?(u.y<v.y?inf:-inf):1.0*(v.y-u.y)/(v.x-u.x); 22 } 23 LL get(LL k) { 24 while (l<r&&slope(st[l],st[l+1])>1.0*k) l++; 25 return st[l].y-k*st[l].x; 26 } 27 void insert(point cur) { 28 while (l<r&&slope(st[r-1],st[r])<slope(st[r-1],cur)) r--; 29 st[++r]=cur; 30 } 31 int main() { 32 n=read(),A=read(),B=read(),C=read(),ratio=A*2,s[0]=0; 33 for (int i=1; i<=n; i++) s[i]=s[i-1]+read(); 34 for (int i=1; i<=n; i++) X[i]=A*sqr(s[i]); 35 for (int i=1; i<=n; i++) Y[i]=B*s[i]; 36 for (int i=1; i<=n; i++) P[i]=X[i]+Y[i]; 37 for (int i=1; i<=n; i++) Q[i]=X[i]-Y[i]; 38 l=1,r=0,st[++r]=point(0,0); 39 for (int i=1; i<=n; i++) { 40 f[i]=get(ratio*s[i])+P[i]+C; 41 insert(point(s[i],f[i]+Q[i])); 42 } 43 printf("%lld\n",f[n]); 44 return 0; 45 }View Code
[luogu P3628] [APIO2010]特別行動隊