很容易发现,如果怪物区间严格不相交,则是一个非常简单的枚举题目。
因为中间空的时间再多,我们也只能上一次子弹,也就是说,每波怪物开始的时候,最多只能存k发子弹。
而题目又保证了只能相交于上一波的终点。
显然我们可以倒着处理,判断每波开始之前,枪里至少要有多少发子弹。
若某一波是独立的则dp[i]=max(0ll,p[i].a-(p[i].r-p[i].l)*k); 除了第一秒后面每秒都能上一次弹。
第一秒需要的子弹数就是dp[i].
而如果当前波结束的时间就是下一波开始的时间,则这两波是关联的,即这一波不仅要打完当前怪兽,还要在最后一天剩下dp[i+1]发子弹(因为中间没有空隙上子弹),则这一波的dp[i]=max(0ll,p[i].a+dp[i+1]-(p[i].r-p[i].l)*k);
若某一波的dp[i]>k则说明无论前面怎么规划,都无法打完所有怪兽。
否则一定能打完,(因为连续的波数我们dp[i]进行了约束,不连续的中间一定有一次上弹的机会,也能满足dp[i])
最后再正向扫一遍累加结果即可!
复杂度On。。。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define re register
#define ls (o<<1)
#define rs (o<<1|1)
//#define m (l+r)/2
#define pb push_back
typedef pair<int,int> pii;
const double PI= acos(-1.0);
const int M = 2000+7;
/*
int head[M],cnt=1;
void init(int n){cnt=1;for(int i=0;i<=n;i++)head[i]=0;}
struct EDGE{int to,nxt,w;}ee[M*2];
void add(int x,int y,int w){ee[++cnt].nxt=head[x],ee[cnt].w=w,ee[cnt].to=y,head[x]=cnt;}
*/
struct node{
ll l,r,a;
}p[M];
ll dp[M];
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
ll n,k;
cin>>n>>k;
for(int i=1;i<=n;i++){
cin>>p[i].l>>p[i].r>>p[i].a;
}
bool flag=true;
for(int i=n;i>=1;i--){
if(p[i].r==p[i+1].l)
dp[i]=max(0ll,p[i].a+dp[i+1]-(p[i].r-p[i].l)*k);
else
dp[i]=max(0ll,p[i].a-(p[i].r-p[i].l)*k);
if(dp[i]>k)flag=false;
}
if(!flag){
cout<<-1<<endl;//不可能打完所有怪物
return 0;
}
ll ans=0,tp=k;//总共打多少子弹,当前弹夹有多少子弹
for(int i=1;i<=n;i++){
if(tp<dp[i])ans+=tp,tp=k;//不连续可以上弹,连续的波数倒着dp时已经判断过了
ans+=p[i].a;
if(p[i].a>=tp)p[i].a-=tp,tp=k-p[i].a%k;
else tp-=p[i].a;
// cout<<i<<" -> "<<p[i].l<<" "<<p[i].r<<" "<<p[i].a<<" "<<tp<<" = "<<ans<<endl;
}
cout<<ans<<endl;
return 0;
}