Gift (单调队列优化dp)

时间:2021-03-26 17:39:05

10.10

思路:
很明显得状态定义以及转移关系
Dp[i][j]表示前i个人装饰前j个单位能得到的最大魅力度
Dp[i][j]=max(dp[i-1][k]+(j-k)*w);(k+1<=原始位置<=j)
经过变形,可以得到dp[i][j]=max(dp[i-1][k]-k*w)+j*w,对于每一个dp[i][j]来说,j是常量,而k是变量,故将dp[i-1][k]-k*w部分求max,而且根据条件,k是满足条件的连续长度内的数,可以用单调队列优化

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#define N 200010
#define LL long long 
using namespace std;

struct EE{
    int len, b, pos;
}a[N];

int n, m;
int q[N >> 1], h, t;
LL f[105][N];

bool cmp(EE a, EE b){
    return a.pos < b.pos;
}

bool check_h(int i, int j){
    return j-q[h] > a[i].len;
}

bool check_t(int i, int k){
    return f[i-1][k]-k*a[i].b > f[i-1][q[t-1]]-q[t-1]*a[i].b;
}

void solve(){
    sort(a+1, a+m+1, cmp);
    for(int i=1; i<=m; i++){
        h = 1, t = 1;
        for(int j=1; j<=n; j++)
            f[i][j] = f[i-1][j];
        int L = max(1, a[i].pos - a[i].len + 1);
        int R = min(n, a[i].pos + a[i].len - 1);
        for(int k=L-1; k<a[i].pos; k++){
            while(h<t && check_t(i,k)) t--;
            q[t++] = k;
        }
        for(int j=a[i].pos; j<=R; j++){
            while(h<t && check_h(i,j)) h++;
            int k = q[h];
            f[i][j] = max(f[i][j], f[i-1][k] + (j-k) * a[i].b);
        }
        for(int j=R+1; j<=n; j++)
            f[i][j] = max(f[i][j], f[i][j-1]);
    }
    cout << f[m][n] << endl;
}

int main(){
    freopen("gift.in", "r", stdin);
    freopen("gift.out", "w", stdout);
    scanf("%d%d", &n, &m);
    for(int i=1; i<=m; i++)
        scanf("%d%d%d", &a[i].len, &a[i].b, &a[i].pos);
    solve();
    return 0;
}