有n件物品,每件物品价格为a[i],有m张优惠券,第i张可以在前r[i]件物品使用,可以使价格减少w[i],求买k件物品最少要花多少钱(k=1,2,3...n)

题目

法一:线段树维护区间最小值,每次选尽可能用优惠券后的价格最小的物品,然后将该物品价格修改为无穷大(单点修改),将使用过的优惠券的影响消除(区间修改)

#include<bits/stdc++.h>
using namespace std;
#define lson p << 1
#define rson p << 1 | 1
#define int long long
const int maxn = 2e5 + 5, inf = 1e9 + 5;
int t[maxn << 2], lazy[maxn << 2], d[maxn], a[maxn], r[maxn], w[maxn];
struct Node{
	int r, w;
	bool operator < (const Node &x)const{
		return r < x.r;
	}
}b[maxn];
void push_up(int p){
	t[p] = min(t[lson], t[rson]);
}
void push_down(int p){
	lazy[lson] += lazy[p];
	lazy[rson] += lazy[p];
	t[lson] += lazy[p];
	t[rson] += lazy[p];
	lazy[p] = 0;
}
void build(int p, int l, int r){
	if(l == r){
		t[p] = a[l];
		return;
	}
	int mid = (l + r) >> 1;
	build(lson, l, mid);
	build(rson, mid + 1, r);
	push_up(p);
}
pair<int, int> query(int p, int l, int r){
	if(l == r){
		int tmp = t[p];
		t[p] = inf;
		//push_up(p);不用push_up!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
		return {l, tmp};
	}
	int mid = (l + r) >> 1;
	pair<int, int> res;
	push_down(p);
	if(t[lson] < t[rson]) res = query(lson, l, mid);
	else res = query(rson, mid + 1, r);
	push_up(p);
	return res;
}
void update(int p, int l, int r, int L, int R, int val){
	if(L <= l && R >= r){
		t[p] += val;
		lazy[p] += val;
		//push_up(p);不用push_up!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
		return;
	}
	push_down(p);
	int mid = (l + r) >> 1;
	if(L <= mid) update(lson, l, mid, L, R, val);
	if(R >= mid + 1) update(rson, mid + 1, r, L, R, val);
	push_up(p);
}
void solve(){
	int n, m, i, j;
	cin >> n >> m;
	for(i = 1; i <= n; i++){
		cin >> a[i];
		d[i] = 0;
		//cout << a[i] << ' ';
	}
	//cout << '\n';
	for(i = 1; i <= 4 * n; i++){
		lazy[i] = 0;
	}
	for(i = 1; i <= m; i++){
		cin >> b[i].r >> b[i].w;
		d[1] -= b[i].w;
		d[b[i].r + 1] += b[i].w;
	}
	int sum = 0;
	for(i = 1; i <= n; i++){
		sum += d[i];
		a[i] = a[i] + sum;
		//cout << a[i] << ' ';
	}
	sort(b + 1, b + m + 1);
	build(1, 1, n);
	//cout << 8 << ' ' << t[8] << '\n';
	int now = 0;
    for(int k = 1; k <= n; k++){
    	auto pr = query(1, 1, n);
		now += pr.second;
		cout << now << ' ';
		int pos = pr.first;
		int p = lower_bound(b + 1, b + m + 1, Node{pos, 0}) - b;
		//cout << k << ' ' << pos << ' ' << p << ' ' << m << '\n';
		//cout << k << ' ';
//		for(i = 1; i <= 9; i++){
//			cout << t[i] << ' ';
//		}
//		cout << '\n';
		for(i = p; i <= m; i++){
			update(1, 1, n, 1, b[i].r, b[i].w);
		}
		m = p - 1;
	}
	cout << '\n';	
}
//struct ti
//{
//	int w, r;
//}tic[maxn];


signed main()
{
	ios::sync_with_stdio(0);
	cin.tie(0);
	int T;
	cin >> T;
	while(T--)
		solve();
}

法二:

#include <bits/stdc++.h>
using namespace std;
#define int long long
#define endl '\n'
#define IOS                \
	ios::sync_with_stdio(0); \
	cin.tie(0);
#define PII pair<int, int>
#define maxn 200010
int a[maxn];
PII ask[maxn];
signed main(void)
{
	IOS int t;
	cin >> t;
	while (t--)
	{
		int n, m;
		cin >> n >> m;
		for (int i = 1; i <= n; i++)
		{
			cin >> a[i];
		}
		for (int i = 1; i <= m; i++)
		{
			cin >> ask[i].first >> ask[i].second;
		}
		sort(ask + 1, ask + 1 + m);
		int it = 0;
		multiset<int> st;
		for (int i = 1; i <= m; i++)
		{
			while (it < ask[i].first)
			{
				st.insert(a[++it]);
			}
			int x = *st.begin();
			st.erase(st.begin());
			x -= ask[i].second;
			st.insert(x);
		}
		while (it < n)
			st.insert(a[++it]);
		int res = 0;
		// cout<<st.size()<<endl;
		for (auto &x : st)
		{
			res += x;
			cout << res << " ";
		}
		cout << endl;
	}
	return 0;
}

以下是C++代码实现: ```cpp #include <iostream> #include <algorithm> #include <cstring> using namespace std; const int MAXN = 1e6 + 5; int n, m, k; double a[MAXN], b[MAXN], c[MAXN], d[MAXN]; double f[MAXN]; int q[MAXN]; int main() { cin >> n; for (int i = 1; i <= n; i++) cin >> a[i]; cin >> m; for (int i = 1; i <= m; i++) { cin >> b[i] >> c[i]; d[i] = 1.0 * b[i] / c[i]; } cin >> k; int hh = 0, tt = -1; for (int i = 1; i <= n; i++) { while (hh <= tt && i - q[hh] > k) hh++; while (hh <= tt && d[i] < d[q[tt]]) tt--; q[++tt] = i; if (i >= k + 1) f[i] = f[q[hh]] + a[i] * d[q[hh]]; } printf("%.1lf", f[n]); return 0; } ``` 其中,$a_i$ 表示第 $i$ 个商品的原价,$b_i$ 表示第 $i$ 优惠券可以减免的金额,$c_i$ 表示第 $i$ 优惠券的面额,即减免后的金额为 $b_i/c_i$,$k$ 表示一个商品最多可以使用优惠券数。 首先,我们将面额和折扣率的比值计算出来,用数组 $d$ 存储。然后采用单调队列的方式进行动态规划。 定义 $f_i$ 表示 $i$ 个商品使用优惠券后的最小花费。单调队列中存储的是 $i$ 个商品中,最小的 $f_j+d_j \times a_i$,其中 $j=i-k, i-k+1, \cdots, i-1$。 对于每个 $i$,我们需要判断队首元素是否在 $i-k, i-k+1, \cdots, i-1$ 中,如果不在则需要出队。然后再将当元素插入队尾,但要保证队列中的元素按照 $d_j$ 的大小单调不降。队首元素就是最小的 $f_j+d_j \times a_i$,最后的答案就是 $f_n$。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

__night_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值