[NOI2017] 蔬菜 题解
神仙贪心题,我这种蒟蒻只能写一篇题解来 \(\text{orz}\) 。
思路
很容易发现,我们做这道题时,正着去递推贪心及其困难。
尤其还有一些过期等限制,解决起来及其复杂。
那么我们就能有一个小小的思路:时光倒流。
我们从反着来递推贪心。
可以发现,过期这个限制就变成了出现。
是不是瞬间就简单多了。
实现步骤
首先有一个大致的步骤:
-
算出第十万天的答案。
-
不断往前递推,算出前面所有的贡献,列出答案表。
-
最后 \(O(1)\) 查询答案
我们可以从 \(p=10^5\) 开始从后往前推。
首先要记录每个时刻出现那些菜,可以用 \(\text{vector}\)。
我们考虑将每一个时刻选的的菜用优先队列来维护。
一开始从 \(\text{vector}\) 加入的菜的值是 \(a_i+s_i\)
因为它是第一次出现。
然后,在循环过程中,每次拿出堆顶最大的值。
我们同样还要记录这个菜有没有被选过。
如果拿出的菜没有被选过,就可以直接把这一个记录答案,剩下的直接以 \(a_i\) 为权值插入优先队列。
如果拿出的菜被选过了,就能取多少取多少。
注意上述过程的答案全部都是给第十万天加的贡献。
递推非常简单。
每次删掉贡献最少的菜就可以了。
Code
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int maxn = 100010;
int n , m , k , a[maxn] , s[maxn] , c[maxn] , x[maxn] , ans[maxn];
int p = 100000 , now , sum[maxn] , used[maxn];
queue<pair<int , int> > r;
vector<int> l[maxn];
priority_queue< pair<int , int> > q;
priority_queue< pair<int , int> , vector<pair<int , int> > , greater< pair<int , int> > > q2;
inline int read()
{
int asd = 0 , qwe = 1; char zxc;
while(!isdigit(zxc = getchar())) if(zxc == '-') qwe = -1;
while(isdigit(zxc)) asd = asd * 10 + zxc - '0' , zxc = getchar();
return asd * qwe;
}
signed main()
{
// freopen("1.in" , "r" , stdin);
// freopen("1.out" , "w" , stdout);
n = read() , m = read() , k = read();
for(int i = 1;i <= n;i++)
{
a[i] = read() , s[i] = read() , c[i] = read() , x[i] = read();
if(x[i] == 0) l[p].push_back(i);
else l[min(p , (c[i] + x[i] - 1) / x[i])].push_back(i);
}
for(int i = p;i >= 1;i--)
{
for(auto j : l[i]) q.push(make_pair(a[j] + s[j] , j));
if(q.empty()) continue;
for(int tim = m;tim >= 1 && q.empty() == 0;q.pop())
{
int top = q.top().second;
// cout << top << " " << tim << " " << used[top] << " " << a[top] << " " << s[top] << " " << i << " " << ans[p] << endl;
if(used[top] == 0)
{
tim--;
ans[p] += (a[top] + s[top]) , sum[top]++ , used[top] = 1;
if(sum[top] != c[top]) q.push(make_pair(a[top] , top));
}
else
{
int last = c[top] - (i - 1) * x[top] - sum[top];
int del = min(last , tim);
ans[p] += del * a[top] , sum[top] += del , tim -= del;
if(sum[top] != c[top]) r.push(make_pair(a[top] , top));
}
}
while(!r.empty()) q.push(r.front()) , r.pop();
}
for(int i = 1;i <= n;i++)
{
now += sum[i];
if(sum[i] == 1) q2.push(make_pair(a[i] + s[i] , i));
else if(sum[i] > 1) q2.push(make_pair(a[i] , i));
}
for(int i = p - 1;i >= 1;i--)
{
ans[i] = ans[i + 1];
int can_del = now - i * m;
while(can_del > 0)
{
int top = q2.top().second; q2.pop();
if(sum[top] == 1)
{
ans[i] -= (a[top] + s[top]);
can_del-- , now--;
}
else
{
int del = min(can_del , sum[top] - 1);
ans[i] -= a[top] * del;
can_del -= del , sum[top] -= del , now -= del;
if(sum[top] > 1) q2.push(make_pair(a[top] , top));
else if(sum[top] == 1) q2.push(make_pair(a[top] + s[top] , top));
}
}
}
for(int i = 1;i <= k;i++)
{
int x = read();
cout << ans[x] << endl;
}
return 0;
}