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;
}

浙公网安备 33010602011771号