出题人的题解:
20分做法:
大暴力
40分做法:
把食物以时间排序,f[i]表示吃到第i种食物时的最大分数。判断两种食物能否续接,复杂度O(n^2)
100分做法:
发现两种食物之间可以转移,当且仅当
|pi-pj| <= (ti-tj)/2
展开之后就是:
2 * ti-pi >= 2 * tj –pj || 2 * ti+pi >= 2 * tj+pj
于是把2 * ti-pi和2 * ti+pi作为i的权值,按前者排序再按后者用数据结构(树状数组,线段树)维护最大值。注意后者需要离散化
#include
#include
#include
#define LL long long
#define N 200010
using namespace std;
int W, n, b[N], dp[N], ans=0, SUM=0;
struct node{
int sum;
node *ls, *rs;
void update(){
sum = max(ls->sum, rs->sum);
}
}pool[N * 40], *tail = pool, *root;
struct AA{
int a, b, v;
}a[N];
node *build(int lf, int rg){
node *nd = ++tail;
if(lf == rg){
nd->sum = 0;
nd->ls = 0; nd->rs = 0;
return nd;
}
int mid = (lf + rg) >> 1;
nd->ls = build(lf, mid);
nd->rs = build(mid+1, rg);
return nd;
}
void modify(node *nd, int lf, int rg, int pos, int val){
if(lf == rg){
nd->sum = max(nd->sum, val);
return ;
}
int mid = (lf + rg) >> 1;
if(pos <= mid) modify(nd->ls, lf, mid, pos, val);
else modify(nd->rs, mid+1, rg, pos, val);
nd->update();
}
int query(node *nd, int lf, int rg, int L, int R){
if(L <= lf && rg <= R)
return nd->sum;
int mid = (lf + rg) >> 1;
int rt = 0;
if(L <= mid) rt = max(rt, query(nd->ls, lf, mid, L, R));
if(R > mid) rt = max(rt, query(nd->rs, mid+1, rg, L, R));
return rt;
}
bool cmp(AA a, AA b){
if(a.a == b.a) return a.b < b.b;
return a.a < b.a;
}
bool cmp1(int a, int b){
return a < b;
}
int main(){
scanf("%d%d%d", &W, &n, &SUM);
for(int i=1; i<=n; i++){
int t, p, v; scanf("%d%d%d", &t, &p, &a[i].v);
a[i].b = 2 * t + p;
a[i].a = 2 * t - p;
b[i] = a[i].b;
}
root = build(0, n);
sort(b+1, b+n+1, cmp1);
int cnt = unique(b+1, b+n+1) - (b+1);
for(register int i=1; i<=n; i++)
a[i].b = lower_bound(b+1, b+cnt+1, a[i].b) - b;
sort(a+1, a+n+1, cmp);
for(register int i=1; i<=n; i++){
int cc = query(root, 0, n, 0, a[i].b);
dp[i] = max(dp[i], a[i].v + cc);
modify(root, 0, n, a[i].b, dp[i]);
ans = max(ans, dp[i]);
}
if(ans >= SUM) printf("%d\n", ans);
else printf("ALL HAIL KING ARTHUR\n");
return 0;
}