「BSOJ1030」 最大獎勵
阿新 • • 發佈:2018-12-14
題目描述
為慶祝BSOI生日及鼓勵廣大OIER多切題,管理員xinyue決定推出一項獎勵措施:他按照每道題目的難度給其賦了一個分值,第一次AC這道題目的時候作者會得到這道題目的分值。假設大牛叉叉嗯AC了n(n <= 100,000)道題目,它們的分值依次為a1,a2,a3,...,an。叉叉嗯就可以按照順序把它們分成若干段提交給xinyue,假設某一段從i+1到j,它們的分值和為x,那麼叉叉嗯可以得到的獎勵是x*i-T,其中-T是因為xinyue嫌你提交的太多,他比較煩。叉叉嗯大牛希望得到的獎勵之和最大,但她已經AC了太多題目,懶得再程式設計了。這個問題就交給你,大牛希望你能好好解決。
分析
考慮Dp。設
時間複雜度為O(n^2),展開其中會有ij的乘積項,考慮斜率優化Dp。
對於,若在j的決策優於k的決策,則有如下不等式:
,移項得:
所以,維護一個特殊的單調佇列維護即可。
程式碼
#include <cstdio> #include <iostream> using namespace std; int n,t,a[100005]; long long sum[100005],f[100005],q[100005],head,tail; long long read() { long long s=0; char ch=getchar(); int f=1; while (ch<'0'||ch>'9') f=(ch=='-'?-1:1),ch=getchar(); while (ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar(); return s*f; } void write(long long s) { if (s<0) { putchar('-'); write(-s); return; } if (s>9) write(s/10); putchar(s%10+'0'); } long double s(int j,int k) { return ((f[j]-sum[j]*j)-(f[k]-sum[k]*k))/((long double)(k-j)); } int main() { n=read(); t=read(); for (int i=1;i<=n;i++) { a[i]=read(); sum[i]=sum[i-1]+a[i]; } f[0]=0; head=tail=1; q[head]=0; for (int i=1;i<=n;i++) { while (head<tail&&(s(q[head],q[head+1])<=sum[i])) head++; f[i]=f[q[head]]+(sum[i]-sum[q[head]])*q[head]-t; while (head<tail&&s(q[tail-1],q[tail])>=s(q[tail],i)) tail--; q[++tail]=i; } write(f[n]); return 0; }