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;
}
posted @ 2023-11-28 19:11  zifanwang  阅读(83)  评论(0编辑  收藏  举报  来源