[JOISC 2019] 两道料理 / Two Dishes 题解

[JOISC 2019] 两道料理 / Two Dishes
有一个 \(O(nm)\) 的暴力 DP,就是设 \(f_{i,j}\) 为 \(1\) 做了 \(i\) 个步骤,\(2\) 做了 \(j\) 个步骤的时的最大得分。只要处理下每个菜操作所需时间的前缀和就行了。

把这个 DP 看作在网格图上走,从 \((0,0)\) 到 \((n,m)\),只能向下或向右。

对于一个 \(1\) 号菜的操作 \(i\),二分出最大的 \(x\) 满足 \(pa_i+pb_x\le s_i\),则要得到 \(p_i\) 分曲线必须在点 \((i,x)\) 下方。

对于一个 \(2\) 号菜的操作 \(i\),二分出最大的 \(x\) 满足 \(pb_i+pa_x\le t_i\),则要得到 \(q_i\) 分曲线必须在点 \((x,i)\) 上方。

又有上又有下很麻烦,先把 \(1\) 号菜的操作得分都加上。然后改成在点 \((i-1,x+1)\) 上方就会得到 \(-p_i\) 分。

把这些关键点都存下来,做扫描线。用一个线段树存储 \(f_{i,j}\),\(j\) 是线段树下标。对于一个关键点 \((x,y,v)\),先对 \(y\) 这个位置取前缀最大值,因为对 \([y,m]\) 做了区间加操作后,前缀最大性只可能在 \(y\) 处改变。然后就区间加上 \(v\),表示在该点上方的路线可以得分。

要添加一个关键点为 \((n,m,0)\),让 \(m\) 位置取前缀最大值。最后答案为线段树上 \(m\) 位置的值加上 \(\sum p_i\)。

时间复杂度 \(O(n \log n)\)。

#include<bits/stdc++.h>
using namespace std;
#define int long long
#define _int __int128
#define ull unsigned long long
#define pii pair<int,int>
#define fst first
#define scd second
#define pq priority_queue
#define mkp make_pair
#define popcount(x) __builtin_popcount(x)
#define endl '\n'
int n,m,tot,ans;
const int N = 1e6+10,inf=1e18;
int a[N],b[N],s[N],t[N],p[N],q[N],pa[N],pb[N];
int f[N<<2],tag[N<<2];
#define ls p<<1
#define rs p<<1|1
struct node{
    int x,y,v;
}d[2*N];
bool cmp(node a,node b){
    return a.x==b.x?a.y>b.y:a.x<b.x;
}
void pushup(int p){
    f[p]=max(f[ls],f[rs]);
}
void pushdown(int p){
    if(tag[p]){
        f[ls]+=tag[p];f[rs]+=tag[p];
        tag[ls]+=tag[p];tag[rs]+=tag[p];
        tag[p]=0;
    }
}
void modify(int p,int l,int r,int x,int v){
    if(l==r){
        f[p]=v;
        return;
    }
    pushdown(p);
    int mid=(l+r)/2;
    if(x<=mid)modify(ls,l,mid,x,v);
    else modify(rs,mid+1,r,x,v);
    pushup(p);
}
void update(int p,int l,int r,int ql,int qr,int v){
    if(ql<=l&&r<=qr){
        f[p]+=v;tag[p]+=v;
        return;
    }
    pushdown(p);
    int mid=(l+r)/2;
    if(ql<=mid)update(ls,l,mid,ql,qr,v);
    if(qr>mid)update(rs,mid+1,r,ql,qr,v);
    pushup(p);
}
int query(int p,int l,int r,int ql,int qr){
    if(ql<=l&&r<=qr){
        return f[p];
    }
    pushdown(p);
    int mid=(l+r)/2,res=-inf;
    if(ql<=mid)res=max(res,query(ls,l,mid,ql,qr));
    if(qr>mid)res=max(res,query(rs,mid+1,r,ql,qr));
    return res;
}
signed main(){
    ios::sync_with_stdio(0);
    cin.tie(0),cout.tie(0);
    cin>>n>>m;
    for(int i=1;i<=n;i++){
        cin>>a[i]>>s[i]>>p[i];
        pa[i]=pa[i-1]+a[i];
    }
    for(int i=1;i<=m;i++){
        cin>>b[i]>>t[i]>>q[i];
        pb[i]=pb[i-1]+b[i];
    }
    for(int i=1;i<=n;i++){
        if(pa[i]>s[i])continue;
        int l=0,r=m+1,id=m+1;
        ans+=p[i];
        while(l<r-1){
            int mid=(l+r)/2;
            if(pb[mid]+pa[i]>s[i]){
                r=mid;
            }else{
                l=mid;
            }
        }
        if(l+1<=m)d[++tot]={i-1,l+1,-p[i]};
    }
    for(int i=1;i<=m;i++){
        if(pb[i]>t[i])continue;
        int l=0,r=n+1,id=n+1;
        while(l<r-1){
            int mid=(l+r)/2;
            if(pa[mid]+pb[i]>t[i]){
                r=mid;
            }else{
                l=mid;
            }
        }
        d[++tot]={l,i,q[i]};
    }
    d[++tot]={0,0,0};
    d[++tot]={n,m,0};
    sort(d+1,d+1+tot,cmp);
    memset(f,-0x3f,sizeof(f));
    modify(1,0,m,0,0);
    for(int i=1;i<=tot;i++){
        if(d[i].x!=d[i-1].x||d[i].y!=d[i-1].y){
            int pmx=query(1,0,m,0,d[i].y);
            modify(1,0,m,d[i].y,pmx);
        }
        update(1,0,m,d[i].y,m,d[i].v);
    }
    cout<<query(1,0,m,m,m)+ans;
    return 0;
}
posted @ 2026-09-15 17:25  farenara_sayowell  阅读(6)  评论(0)    收藏  举报