CF521D Shop

Description

洛谷传送门

Solution

一道非常友好毒瘤的贪心题。

前置:本文与题目中变量名不同,本文:有 (n) 个正整数,(m) 个操作,选 (k) 个。

题目中要求 3 种操作。

  • (a_i) 赋值为 (b);

  • (a_i) 加上 (b);

  • (a_i) 乘上 (b)

一个显然的贪心,一定是先赋值,再加法,最后乘法。

我们可以从简化版开始考虑。

如果全部都是乘法,那么我们把 (b) 按从大到小排序,取前 (k) 个即可。

但是还有赋值操作和加法操作,那该怎么办呢?

很简单。

假设把 (a) 赋值为 (b),那就相当于 (a = a * frac{b}{a})

(a) 加上 (b),就相当于 (a = a * frac{a + b}{a})

好!这题切了,写代码!交!

……

那这是怎么回事呢?小编也觉得很奇怪。

这大概是因为,我们在做加法时,每次都是对原数进行改动,当存在多个数时,贪心的正确性就无法保证了。

所以加法操作要存下来,从大到小进行排序,然后做个前缀和,再改成乘法形式。

对于赋值操作,我们只记录最大的一次赋值即可,注意要先改成加法,再改成乘法,因为这个最大的赋值也可能不选。

这道题写数组,(vector),或优先队列都可以,我这里用的优先队列(调了好久 (QwQ))。

Code

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <queue>
#define ll long long

using namespace std;

const ll N = 1e5 + 10;
ll n, m, k;
struct node{
	ll id, x, type;
	double val;
	bool operator < (const node &b) const{
		return val < b.val;
	}
};
struct Ans{
	ll type, pos;
	bool operator < (const Ans &b) const{
		return type != b.type ? type < b.type : pos < b.pos;
	}
}ans[N];
priority_queue <node> q, q2, q3[N];//q:乘法  q2:赋值  q3:加法
double a[N], b[N];
ll cnt;
bool vis[N];//vis[i]:记录第 i 个数的最大赋值操作是否计算过。

signed main(){
	scanf("%lld%lld%lld", &n, &m, &k);
	for(ll i = 1; i <= n; i++)
		scanf("%lf", &a[i]), b[i] = a[i];
	for(ll i = 1; i <= m; i++){
		ll op, x;
		double y;
		scanf("%lld%lld%lf", &op, &x, &y);
		if(op == 1 && y > a[x]) q2.push((node){i, x, 1, y});
		if(op == 2) q3[x].push((node){i, x, 2, y});
		if(op == 3) q.push((node){i, x, 3, y});
	}
	while(!q2.empty()){//赋值操作改成加法。
		node now = q2.top();
		q2.pop();
		if(!vis[now.x]) vis[now.x] = 1, now.val -= a[now.x], q3[now.x].push(now);//赋值操作改成加法的话,就是 a = b - a
	}
	ll sum = 0;
	for(int i = 1; i <= n; i++){//加法改成乘法
		sum = a[i];
		while(!q3[i].empty()){
			node now = q3[i].top();
			q3[i].pop();
			ll tmp = now.val;//注意先转存一下,不然值就变了,不转存的话也是在第 149 个点Wa。
			now.val = (now.val + sum) / sum;
			sum += tmp;
			q.push(now);
		}
	}
	for(ll i = 1; i <= k && !q.empty(); i++){//取前 k 个。
		node now = q.top(); 
		q.pop();
		ans[++cnt] = (Ans){now.type, now.id};
	}
	sort(ans + 1, ans + 1 + cnt);//题目要求按操作种类从小到大排序,即先赋值,在加法,再乘法
	printf("%lld
", cnt);
	for(ll i = 1; i <= cnt; i++)
		printf("%lld ", ans[i].pos);
	puts("");
	return 0;
}

End

本文来自博客园,作者:xixike,转载请注明原文链接:https://www.cnblogs.com/xixike/p/15378632.html

原文地址:https://www.cnblogs.com/xixike/p/15378632.html