P9871 [NOIP2023] 天天爱打卡 题解
$\large\textbf{Statement.}$
题意,即求出选择若干个互不相交且互不相邻的区间,每个区间的大小不超过 $k$,每个区间覆盖到的挑战的权值和 $-\sum (r-l+1)\cdot d$ 的最大值。
$\large\textbf{Solution.}$
直接 $\tt dp$,令 $f_n$ 表示前 $n$ 天的答案,枚举一个 $r=n$ 的区间的左端点对当前状态进行更新,最后再与 $f_{n-1}$ 取个最大值。
这样就得到了一个 $O(tn^2)$ 的做法,离散化以后可以做到 $O(tm^2)$。
然后就是线段树优化 $\tt dp$ 板子题,时间复杂度 $O(tm\log m)$,可以轻松通过。
参考代码:
#include<bits/stdc++.h>
#define ll long long
#define mxn 100003
#define md 1000000007
#define ld long double
#define pb push_back
#define mkp make_pair
#define umap unordered_map
#define rep(i,a,b) for(int i=a;i<=b;++i)
#define rept(i,a,b) for(int i=a;i<b;++i)
#define drep(i,a,b) for(int i=a;i>=b;--i)
#define pq priority_queue
using namespace std;
struct node{
int x,y,z;
}a[mxn];
int C,T,n,m,k,d,rt,tot,f[mxn<<1];
ll t[mxn<<3],dp[mxn<<1],ad[mxn<<3];
inline void push_down(int p){
if(ad[p]){
ad[p<<1]+=ad[p],t[p<<1]+=ad[p];
ad[p<<1|1]+=ad[p],t[p<<1|1]+=ad[p];
ad[p]=0;
}
}
void build(int p,int l,int r){
ad[p]=0;
if(l==r){
t[p]=(ll)f[l+1]*d;
return;
}
int mid=(l+r)>>1;
build(p<<1,l,mid);
build(p<<1|1,mid+1,r);
t[p]=max(t[p<<1],t[p<<1|1]);
}
void add(int p,int l,int r,int x,int L,int R){
if(l>r)return;
if(l<=L&&R<=r){
ad[p]+=x,t[p]+=x;
return;
}
push_down(p);
int mid=(L+R)>>1;
if(l<=mid)add(p<<1,l,r,x,L,mid);
if(r>mid)add(p<<1|1,l,r,x,mid+1,R);
t[p]=max(t[p<<1],t[p<<1|1]);
}
ll ask(int p,int l,int r,int L,int R){
if(l>r)return -1e18;
if(l<=L&&R<=r)return t[p];
push_down(p);
int mid=(L+R)>>1;
if(l<=mid&&r>mid)return max(ask(p<<1,l,r,L,mid),ask(p<<1|1,l,r,mid+1,R));
if(l<=mid)return ask(p<<1,l,r,L,mid);
return ask(p<<1|1,l,r,mid+1,R);
}
void change(int p,int x,ll y,int l,int r){
if(l==r){t[p]=y;return;}
push_down(p);
int mid=(l+r)>>1;
if(x<=mid)change(p<<1,x,y,l,mid);
else change(p<<1|1,x,y,mid+1,r);
t[p]=max(t[p<<1],t[p<<1|1]);
}
signed main(){
scanf("%d%d",&C,&T);
while(T--){
scanf("%d%d%d%d",&n,&m,&k,&d);
f[tot=1]=0;
rep(i,1,m){
scanf("%d%d%d",&a[i].x,&a[i].y,&a[i].z);
swap(a[i].x,a[i].y);
a[i].x=a[i].y-a[i].x+1;
f[++tot]=a[i].x,f[++tot]=a[i].y;
}
sort(f+1,f+tot+1);
tot=unique(f+1,f+tot+1)-f-1;
build(1,1,tot);
rt=1;
sort(a+1,a+m+1,[](node x,node y){
return x.y<y.y;
});
rep(i,2,tot){
while(rt<=m&&a[rt].y<=f[i]){
add(1,1,lower_bound(f+1,f+tot+1,a[rt].x)-f-1,a[rt].z,1,tot);
rt++;
}
dp[i]=max(ask(1,lower_bound(f+1,f+tot+1,f[i]-k+1)-f-1,i-1,1,tot)-(ll)(f[i]+1)*d,dp[i-1]);
if(f[i]+1==f[i+1])change(1,i,dp[i-1]+(ll)f[i+1]*d,1,tot);
else change(1,i,dp[i]+(ll)f[i+1]*d,1,tot);
}
printf("%lld\n",dp[tot]);
}
return 0;
}