Watching Firework is Fun

题面

题解:
这种题显然就是DP了
以第\(i\)个烟花为阶段,放出第\(i\)个烟花时的位置为状态
决策不明显,其实是去到哪个位置为决策
状态转移方程就是:
\(f[i][j]=max(f[i-1][k])+b-|a-j|\)
于是朴素的状态转移方程就有了

这个状态转移方程需要枚举三个维度,时间复杂度为\(O(n^2m)\)级别
但是,我们的数据范围:
\(n\leqslant 150000,m\leqslant 300\)
这个时间复杂度显然直接炸飞了
期望的时间复杂度应该是\(O(nm)\)或者\(O(m^3)\)

考虑优化
注意到状态转移方程中状态和决策维是\(n\)级别
又状态转移方程中的\(k\)是一个区间,所以可以上线段树维护
于是时间复杂度为\(O(nmlog n)\)级别,但是:
\(n*m*logn\leqslant7.8e8\)
时限\(1s\)显然跑不过,需要更好的优化

注意到状态转移方程中
\(max(f[i-1][k])\)仅与决策维\(k\)相关,也就是说:
这个状态转移方程是\(1D/1D\)
换句话说:
可以使用单调队列维护最优决策
这样,由于每个决策只会出入队一次,决策维转移为\(O(1)\)
时间复杂度为\(O(nm)\)

点击查看代码
#include <bits/stdc++.h>
using namespace std;
#define ll long long 
const int o=1.5e5+10;
ll f[310][o];
int main(){
	freopen("fire.in","r",stdin);
	freopen("fire.out","w",stdout);
    int n,m,d,p=0;
    scanf("%d%d%d",&n,&m,&d);
    for(int i=1;i<=m;i++){
        int a,b,t;
        scanf("%d%d%d",&a,&b,&t);
        ll H=(long long)d*(t-p);
        p=t;
        ll q[o],hd=1,tl=0;int k=1;
        for(int j=1;j<=n;j++){
            for(;k<=n&&k<=j+H;k++){
                while(hd<=tl&&f[i-1][k]>=f[i-1][q[tl]]){
                    tl--;
                }
                q[++tl]=k;
            }
            while(hd<=tl&&q[hd]<j-H){
                hd++;
            }
            f[i][j]=f[i-1][q[hd]]+b-abs(a-j);
        }
    }
    ll ans=-1e18;
    for(int i=1;i<=n;i++){
        ans=max(ans,f[m][i]);
    }
    cout<<ans;
    return 0;
}
posted @ 2022-06-07 18:07  2K22  阅读(29)  评论(0)    收藏  举报