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;
}
posted @ 2026-05-22 16:40  WinterXorSnow  阅读(7)  评论(0)    收藏  举报