CodeForces - 372C Watching Fireworks is Fun

Posted on 2022-11-17 20:10  Capterlliar  阅读(28)  评论(0编辑  收藏  举报

题意:有n个点,其中m个要放烟花。每个放烟花的点有属性b[i],放的时间t[i]和位置a[i]。假设放烟花的时候你在位置x,那么可以获得快乐b[i]-|x-a[i]|。你走来走去地看烟花,起始位置任意,每秒最多能走d个点。求最多能获得多少快乐。

解:先设dp[i][j]为放到第i个烟花,你在位置j,能获得的最大欢乐值。不难推出 dp[i][j]=max(dp[i-1][k]+b[i]-|a[i]-j|), k 属于 [j-d, j+d]。直接转移时间复杂度会炸,用线段树优化也会,考虑到每次要用的区间都向右移了一格,且b[i]-|a[i]-j|是常数,可以用单调队列求dp[i-1][k]的最大值。当前点两边的值都能取,因此做两遍。然后优化空间,滚动一下数组。

代码:

#include <bits/stdc++.h>
using namespace std;
#define maxx 150005
#define maxn 25
#define maxm 205
#define ll long long
#define inf 1000000009
#define mod 2520
ll a[305],b[305],t[305];
ll dp[2][maxx];
int q[maxx]={0};
signed main() {
    int n,m,d;
    scanf("%d%d%d",&n,&m,&d);
    for(int i=1;i<=m;i++){
        scanf("%I64d%I64d%I64d",&a[i],&b[i],&t[i]);
    }
    memset(dp,-0x3f3f3f3f,sizeof dp);
    for(int i=1;i<=n;i++){
        dp[1][i]=b[1]-abs(a[1]-i);
    }
    int c=0;
    for(int i=2;i<=m;i++){
        ll len=(t[i]-t[i-1])*d;
        int l=1,r=0;
        for(int j=1;j<=n;j++){
            while(l<=r&&q[l]<j-len) l++;
            while(l<=r&&dp[1-c][q[r]]<dp[1-c][j]) r--;
            q[++r]=j;
            dp[c][j]=dp[1-c][q[l]]+b[i]-abs(a[i]-j);
        }
        l=1,r=0;
        for(int j=n;j>=1;j--){
            while(l<=r&&q[l]>j+len) l++;
            while(l<=r&&dp[1-c][q[r]]<dp[1-c][j]) r--;
            q[++r]=j;
            dp[c][j]=max(dp[c][j],dp[1-c][q[l]]+b[i]-abs(a[i]-j));
        }
        c=1-c;
    }
    ll ans=-1e18;
    for(int i=1;i<=n;i++){
        ans=max(ans,dp[1-c][i]);
    }
    printf("%I64d\n",ans);
    return 0;
}
//dp[i][j] max value of the i th firework, in the j th section
//dp[i][j]=max(dp[i-1][k]+b[i]-|a[i]-j|), k is in [j-d, j+d]
View Code