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\n", cnt);
for(ll i = 1; i <= cnt; i++)
printf("%lld ", ans[i].pos);
puts("");
return 0;
}