hdu6619(贪心+斜率优化)

题目链接

http://acm.hdu.edu.cn/showproblem.php?pid=6619

题意

有 $n$ 棵树,高度分别为 $h_i$ ,需要依次跳过这 $n$ 棵树,每次跳跃体力值减 $h_i$ (体力值可以为负),每次跳跃之后的体力值做为分数累计

每次跳跃之前,可以选择吃掉这棵树,最多吃 $M$ 次,此时树的高度为零

每次跳跃后,可以选择休息,最多休息 $K$ 次,体力值恢复成之前吃掉的树的高度和

求最大的分数

$n\le10^4,M\le50,K\le50$

题解

前面的结论感觉就十分的卡人。。

首先吃和休息这两个操作是独立的。。对一个吃来说,如果后面没有休息,那么他产生的贡献显然是 $h_i(n-i+1)$ ,如果有休息,本来恢复成 $0$ 的体力值恢复成了 $h_i$ ,即后面的点不管有没有经过休息,其体力值都比原来多了 $h_i$ ,所以吃的贡献永远都是 $h_i(n-i+1)$ ,所以对吃直接选最大的贡献吃了就可以了。。

然后问题就简单很多了。。对休息,我们设 $d[i][j]$ 为到 $i$ 休息了 $j$ 次的最大分数,列完方程直接斜率优化即可。。




代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
/**
*         ┏┓    ┏┓
*         ┏┛┗━━━━━━━┛┗━━━┓
*         ┃       ┃  
*         ┃   ━    ┃
*         ┃ >   < ┃
*         ┃       ┃
*         ┃... ⌒ ...  ┃
*         ┃ ┃
*         ┗━┓ ┏━┛
*          ┃ ┃ Code is far away from bug with the animal protecting          
*          ┃ ┃ 神兽保佑,代码无bug
*          ┃ ┃           
*          ┃ ┃       
*          ┃ ┃
*          ┃ ┃           
*          ┃ ┗━━━┓
*          ┃ ┣┓
*          ┃ ┏┛
*          ┗┓┓┏━━━━━━━━┳┓┏┛
*           ┃┫┫ ┃┫┫
*           ┗┻┛ ┗┻┛
*/

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<queue>
#include<map>
#include<stack>
#include<cmath>
#include<set>
#include<bitset>
#include<complex>
#include<cstdlib>
#include<assert.h>
#define inc(i,l,r) for(int i=l;i<=r;i++)
#define dec(i,l,r) for(int i=l;i>=r;i--)
#define link(x) for(edge *j=h[x];j;j=j->next)
#define mem(a) memset(a,0,sizeof(a))
#define ll long long
#define eps 1e-8
#define succ(x) (1<<x)
#define lowbit(x) (x&(-x))
#define mid (x+y>>1)
#define sqr(x) ((x)*(x))
#define NM 100005
#define nm 2005
using namespace std;
const double pi=acos(-1);
const ll inf=1e18;
ll read(){
ll x=0,f=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
while(isdigit(ch))x=x*10+ch-'0',ch=getchar();
return f*x;
}


int n,m,p,q[NM],qh,qt;
ll d[NM],g[NM],ans,a[NM],b[NM];

double slope(int x,int y){return 1.0*(g[x]-b[x]-g[y]+b[y])/(a[x]-a[y]);}

int main(){
int _=read();while(_--){
n=read();m=read()+1;p=read();
inc(i,1,n)a[i]=read(),b[i]=a[i]*(n-i+1);
sort(b+1,b+1+n,greater<ll>());
ans=0;
inc(i,1,p)ans+=b[i];
inc(i,1,n)b[i]=i*a[i]+b[i-1],a[i]+=a[i-1];
inc(i,1,n)d[i]=-inf;
inc(j,1,m){
inc(i,1,n)g[i]=d[i];g[0]=0;
q[qh=qt=1]=0;
inc(i,1,n){
while(qh<qt&&slope(q[qh],q[qh+1])>-i-1)qh++;
d[i]=g[q[qh]]-(i+1)*(a[i]-a[q[qh]])+b[i]-b[q[qh]];
while(qh<qt&&slope(q[qt-1],q[qt])<slope(q[qt],i))qt--;
q[++qt]=i;
}
}
ans+=d[n];
printf("%lld\n",ans);
}
return 0;
}