[APIO2010] 特別行動隊


題目描述

  你有一支由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。


題目分析

被這題坑慘了。
容易寫出動規方程
fi=max fj+a(sumisumj)2+b(sumisumj)+c
考慮斜率優化
化簡爲
fiasum2ibsumic=fj+asum2j2asumisumjbsumj
fj+asum2jbsumj=y
sumi=k
2asumj=x
因爲k單調遞增且爲正,維護下凸包
當然也可以令2asumi=k sumj=x
但因爲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;
}

發佈了169 篇原創文章 · 獲贊 31 · 訪問量 7萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章