P16370 OOI 2026 Monsters and Swords Sol
题目链接。
不会做紫题喵,DP 转移不动谢谢喵。
紫题大人,血脉压制的。但感谢星怒 Concert_B 的讲解。
本题有两种状态设计,一种设 \(f_i\) 表示处理完前 \(i\) 个怪剩下的最大费用(手上没有剩下剑),另一种表示处理完 \(i\) 到 \(n\) 个怪需要的最小费用。由于我太区了,所以使用第一种。
定义 \(cost(x)\) 表示购买强度大于等于 \(x\) 的剑的最小费用,\(pre(i)\) 表示 \(i\) 之前怪物爆的金币总数。则有:
\(f_i =pre(i) + \max\limits_{j=i-k}^{i-1} f_j - pre(j) + cost(\max\limits_{k=j+1}^i h_k)\)。(虽然我赛时没有想到前缀和)。
当然我们还有限制,要 \(f_j \ge cost(\max\limits_{k=j+1}^i h_k)\)。因此考虑上线段树维护每一个节点的 \(\max\limits_{j=i-k}^{i-1} f_j - pre(j) + cost(\max\limits_{k=j+1}^i h_k)\) 的最大值和 \(f_j - cost(\max\limits_{k=j+1}^i h_k)\) 的最小值。对于最小值小于 0 的暴力删去即可。对于 \(cost\) 的维护,使用单调栈维护就好了。
时间复杂度 \(O(n \log n)\) 级别。
Code
#include<bits/stdc++.h>
using namespace std;
#define IOS ios::sync_with_stdio(false);cin.tie(0),cout.tie(0)
#define File(s) freopen(s".in","r",stdin);freopen(s".out","w",stdout)
#define LL long long
#define fi first
#define se second
const LL INF = 1e18;
const int N = 5e5 + 10;
LL h[N],r[N];
int n,m,k;
LL x;
struct jian{
LL s,c;
}a[N],b[N];
int tot = 0;
bool cmp(jian x,jian y){
if(x.s == y.s) return x.c > y.c;
return x.s < y.s;
}
bool cmp2(jian x,jian y){
return x.s < y.s;
}
struct segment_tree{
LL mx[N << 2],mn[N << 2],lzy[N << 2];
segment_tree(){fill(mx,mx+(N<<2),-INF);fill(mn,mn+(N<<2),INF);}
void pushup(int p){
mx[p] = max(mx[p*2],mx[p*2+1]);
mn[p] = min(mn[p*2],mn[p*2+1]);
}
void pushdown(int p){
if(lzy[p] == 0) return ;
mx[p*2] += lzy[p];
mn[p*2] += lzy[p];
lzy[p*2] += lzy[p];
mx[p*2+1] += lzy[p];
mn[p*2+1] += lzy[p];
lzy[p*2+1] += lzy[p];
lzy[p] = 0;
}
void update(int x,int pl,int pr,int p,LL k,LL kk){
if(pl == pr){
mx[p] = k;
mn[p] = kk;
lzy[p] = 0;
return ;
}
pushdown(p);
int mid = pl + pr >> 1;
if(x <= mid) update(x,pl,mid,p*2,k,kk);
else update(x,mid+1,pr,p*2+1,k,kk);
pushup(p);
}
void del(int pl,int pr,int p){
if(mn[p] >= 0) return ;
if(pl == pr){
mx[p] = -INF;
mn[p] = INF;
lzy[p] = 0;
return ;
}
pushdown(p);
int mid = pl + pr >> 1;
del(pl,mid,p*2);
del(mid+1,pr,p*2+1);
pushup(p);
}
void add(int L,int R,int pl,int pr,int p,LL k){
if(L <= pl && pr <= R){
mx[p] += k;
mn[p] += k;
lzy[p] += k;
if(mn[p] < 0)
del(pl,pr,p);
return ;
}
pushdown(p);
int mid = pl + pr >> 1;
if(L <= mid)
add(L,R,pl,mid,p*2,k);
if(R > mid)
add(L,R,mid+1,pr,p*2+1,k);
pushup(p);
return ;
}
LL query(int L,int R,int pl,int pr,int p){
if(L <= pl && pr <= R){
return mx[p];
}
pushdown(p);
int mid = pl + pr >> 1;
LL res = -INF;
if(L <= mid)
res = max(res,query(L,R,pl,mid,p*2));
if(R > mid)
res = max(res,query(L,R,mid+1,pr,p*2+1));
return res;
}
}sgt;
stack< array<int,3> > sta;
LL f[N],sum[N],ned[N];
int main(){
IOS;
File("swords");
cin >> n >> m >> k >> x;
for(int i=1;i<=n;i++){
cin >> h[i] >> r[i];
sum[i] = sum[i-1] + r[i];
}
for(int i=1;i<=m;i++)
cin >> a[i].s >> a[i].c;
sort(a+1,a+1+m,cmp);
b[1] = a[m];
tot = 1;
for(int i=m-1;i>=1;i--){
if(a[i].c < b[tot].c)
b[++tot] = a[i];
}
sort(b+1,b+1+tot,cmp2);
for(int i=1;i<=n;i++){
int L=1,R=tot,fnd = -1;
while(L <= R){
int mid = L + R >> 1;
if(b[mid].s >= h[i]){
fnd = mid;
R = mid - 1;
}
else
L = mid + 1;
}
if(fnd == -1){
cout << "No\n";
return 0;
}
ned[i] = fnd;
}
sta.push({0,0,0});
f[0] = x;
sgt.update(0, 0, n, 1, x, x);
for(int i=1;i<=n;i++){
int nw = i - 1;
while(!sta.empty() && ned[i] >= sta.top()[2]){
auto [L,R,v] = sta.top();
sta.pop();
nw = L;
sgt.add(L,R,0,n,1,-(b[ned[i]].c - b[v].c));
}
sta.push({nw,i-1,int(ned[i])});
f[i] = sgt.query(max(i-k,0),i-1,0,n,1) + sum[i];
sgt.update(i,0,n,1,f[i] - sum[i],f[i]);
sta.push({i,i,0});
}
if(f[n] < 0) cout << "No\n";
else cout << "Yes\n";
return 0;
}

浙公网安备 33010602011771号