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

浙公网安备 33010602011771号