saber (线段树维护dp)

saber

出题人的题解:
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;
} 

你可能感兴趣的:(—————练习赛—————,—————dp—————,线段树,7gOJ)