Loading

JOISC2019 Day2T2 ふたつの料理(Two Dishes)

。对每个步骤 \(i\) 处理出 \(r_i:\) 答案有贡献 \(p_i\) 仅当盖饭做完第 \(i\) 步时咖喱完成了不超过前 \(j\) 步,\(r_j\) 同理,容易想到网格图上走路,找到一条 \((0,0)\)\((n,m)\) 的路径,\((i,r_i)\) 在路径上方(非严格,被路径覆盖也可)有 \(p_i\) 贡献,\((r_j,j)\) 在下方(非严格)有 \(q_j\) 贡献。考虑统一口径,默认 \(p_i\) 全部选中,盖饭的点相当于在下方(严格)贡献 \(-p_i\),这些点往左上移一格就是下方(非严格)。问题就是选出路径最大化下方(非严格)的点权和。

接下来开始迷惑。全网所有笔记题解都宣称,自然的想法是维护差分+线段树二分,掏的转移是类似 \(f_{i,j}=\max(f_{i,j-1},f_{i-1,j}+s_{i-1,j})\)\(s_{i,j}:(i,j)\) 正下方点权和),把 \((i-1,j)\) 拐点(|▔)贡献延后到 \(i\) 去算,相当于先加再取前缀 \(\max\),令人不解。拐点只可能是 \(\mathcal O(n+m)\) 个关键点中一个,\(f_{i,j}\) 定义不变,扫到当前列 \(i\) 你就从上到下枚举关键点 \((i,j)\),令 \(f_{i,j}\gets \max_{k\le j}f_{i-1,k}\),然后把 \(f_{i,k}(k\ge j)\) 全部加上 \((i,j)\) 的点权即可。核心逻辑就是,在线段树上显式维护前缀 \(\max\) 这种东西很繁琐,可以先不管,需要前缀 \(\max\) 的时候再查一下掏出来用。如下代码是正确的

#include <cstdio>
#include <algorithm>
#include <map>
#define ll long long

using namespace std;

template<class tp>void re(tp &x){
	x=0;bool f=0;int c=getchar();
	while(c<'0'||c>'9') f|=c=='-',c=getchar();
	while(c>='0'&&c<='9') x=(x<<3)+(x<<1)+(c^48),c=getchar();
	if(f) x=-x;
}

const int N=1048576;

namespace sgt{
	#define ls(x) (x<<1)
	#define rs(x) (x<<1|1)
	struct node{
		ll mx,tg;
		#define mx(x) T[x].mx
		#define tg(x) T[x].tg 
	}T[N<<2];
	void ad(int x,ll p){mx(x)+=p;tg(x)+=p;}
	void downtag(int x){if(tg(x)) ad(ls(x),tg(x)),ad(rs(x),tg(x)),tg(x)=0;}
	void pls(int now,int ln,int rn,int y,ll p){
		if(y<=ln) return ad(now,p);
		downtag(now);int mid=ln+rn>>1;
		pls(rs(now),mid+1,rn,y,p);
		if(y<=mid) pls(ls(now),ln,mid,y,p);
		mx(now)=max(mx(ls(now)),mx(rs(now)));
	}
	void upd(int now,int ln,int rn,int y,ll p){
		if(ln==rn) return mx(now)=p,void();
		downtag(now);int mid=ln+rn>>1;
		if(y<=mid) upd(ls(now),ln,mid,y,p);
		else upd(rs(now),mid+1,rn,y,p);
		mx(now)=max(mx(ls(now)),mx(rs(now)));
	}
	ll qry(int now,int ln,int rn,int y){
		if(ln==rn) return mx(now);
		downtag(now);int mid=ln+rn>>1;
		if(y<=mid) return qry(ls(now),ln,mid,y);
		else return qry(rs(now),mid+1,rn,y);
	}
	ll mux(int now,int ln,int rn,int y){
		if(rn<=y) return mx(now);
		downtag(now);int mid=ln+rn>>1;ll res=-1e18;
		if(y>mid) res=mux(rs(now),mid+1,rn,y);
		return max(res,mux(ls(now),ln,mid,y));
	}
}using namespace sgt;

map<int,ll,greater<int> > mp[N];

ll a[N],s[N],b[N],t[N];int p[N],q[N];

//#include "xzr.hpp"

int main()
{
	//wxd;//Xzr;
	int n,m;re(n);re(m);mp[n][m]=0;ll ans=0;
	for(int i=1;i<=n;++i) re(a[i]),a[i]+=a[i-1],re(s[i]),re(p[i]),ans+=p[i];
	for(int i=1;i<=m;++i) re(b[i]),b[i]+=b[i-1],re(t[i]),re(q[i]);
	for(int i=1;i<=n;++i) a[i]<=s[i]?mp[i-1][upper_bound(b,b+m+1,s[i]-a[i])-b]-=p[i]:ans-=p[i];
	for(int i=1;i<=m;++i) b[i]<=t[i]?mp[upper_bound(a,a+n+1,t[i]-b[i])-a-1][i]+=q[i]:0;
	for(int i=0;i<=n;++i) for(auto[y,o]:mp[i]) if(y<=m) upd(1,0,m,y,mux(1,0,m,y)),pls(1,0,m,y,o);
	printf("%lld",ans+qry(1,0,m,m));
}
posted @ 2024-11-04 15:56  Albertvαn  阅读(26)  评论(0编辑  收藏  举报