题解:AT_joisc2019_e ふたつの料理 (Two Dishes)
OldDriverTree · · 题解
Solution
令
考虑这样一个暴力 DP:设
先对
具体来讲就是每次先转移
然后考虑如果每次进行一次后缀加然后立刻进行前缀 max 怎么做,我们可以用一个 set 维护所有差分不为
对于相邻两次前缀 max 间有多次后缀加的情况,我们只需要先加正数再加负数即可。然后因为操作总数不超过
Code
//when you use vector or deque,pay attention to the size of it.
//by OldDirverTree
#include<bits/stdc++.h>
//#include<atcoder/all>
#define P pair<int,int>
#define int long long
#define mid (l+r>>1)
using namespace std;
//using namespace atcoder;
//using mint=modint998244353;
const int N=1e6+1;
vector<P> C[N]; map<int,int> f;
int n,m,ans,a[N],b[N],s[N],t[N],p[N],q[N];
struct custom_hash
{
static uint64_t splitmix64(uint64_t x) {
x+=0x9e3779b97f4a7c15;
x=(x^(x>>30) )*0xbf58476d1ce4e5b9;
x=(x^(x>>27) )*0x94d049bb133111eb;
return x^(x>>31);
}
size_t operator() (uint64_t x) const {
static const uint64_t FIXED_RANDOM=chrono::steady_clock::now().time_since_epoch().count();
return splitmix64(x+FIXED_RANDOM);
}
};
int read() {
int x=0; bool _=true; char c=0;
while (!isdigit(c) ) _&=(c!='-'),c=getchar();
while (isdigit(c) ) x=x*10+(c&15),c=getchar();
return _?x:-x;
}
main()
{
n=read(),m=read();
for (int i=1;i<=n;i++) a[i]=a[i-1]+read(),s[i]=read(),p[i]=read();
for (int i=1;i<=m;i++) b[i]=b[i-1]+read(),t[i]=read(),q[i]=read();
for (int i=1;i<=n;i++) if (s[i]>=a[i]) {
int pos=upper_bound(b+1,b+m+1,s[i]-a[i])-b;
if (pos<=m) C[i].push_back({-p[i],pos}); ans+=p[i];
}
for (int i=1;i<=m;i++) if (t[i]>=b[i]) {
int pos=upper_bound(a+1,a+n+1,t[i]-b[i])-a;
if (pos<=n) C[pos].push_back({q[i],i}); else ans+=q[i];
}
for (int i=1;i<=n;i++)
{
for (auto [val,pos]:C[i]) if (val>0) f[pos]+=val;
for (auto [val,pos]:C[i]) if (val<0) {
for (auto it=f.lower_bound(pos);val<=0&&it!=f.end();it=f.erase(it) )
pos=it->first,val+=it->second; if (val>0) f[pos]+=val;
}
}
for (auto [x,y]:f) ans+=y;
return !printf("%lld",ans);
}