题目
思路一
感谢@Tyher 的博客提供了思路。
当然咯,我也有想过“时光倒流”,不过最后没有成型罢了。
我们从后往前考虑每一天的行为。感觉和 “图的连通性” 问题中,只有删边时类似。
对于最后一天来说,我现在可以随便卖 m m m 种蔬菜,因为 蔬菜只会源源不断地增加,不会减少。也就是说,现在能卖的,“以后”还是能卖。所以可以贪心地选择价值最大的蔬菜。
用堆来维护即可。第一次卖出之前,将价值设置为 ( s i + a i ) (s_i+a_i) (si+ai) ,其他情况下为 a i a_i ai 。
这个设置是没有问题的唷!因为这个价值 ( s i + a i ) (s_i+a_i) (si+ai) 即使我在“以后”卖掉也不会变。——我反正是没有想到这一点。
然而 k k k 最大可达 1 0 5 10^5 105 (毕竟 p j p_j pj 互不相同)。这提示我们离线处理出所有 p j p_j pj 的情况。
如何递推呢?从 ( p + 1 ) (p+1) (p+1) 天的情况转移到 p p p 天的情况?
只需要强制总卖出数量降为 m p mp mp 即可。并且无论是哪种蔬菜都可以,因为蔬菜可以卖的更早。你只需要将空腾出来,然后依次填上即可。
似乎有点晦涩。仔细想想 “从后往前考虑” 的情形,你就会明白的!不妨想象成 p × m p\times m p×m 的棋盘上放置棋子,有一些棋子只能放在 1 1 1 到 r r r 行。留空总有人填。
思路二
感谢@ustze 的博客提供了思路。
贵的菜要多卖。毫无争议的一句话。什么时候卖呢?越晚越好。
这很像 不守交规 啊!所以贪心地将蔬菜按照权值排序,并查集查询最晚的可售卖日期。
第一次售卖的蔬菜,将权值赋值为 ( s i + a i ) (s_i+a_i) (si+ai) 即可。排序利用堆即可。
问题又是如何递推。但是它的递推十分简单。就像 思路一
中所说,你只需要随便拿走几个棋子,腾出空来即可。我拿走的棋子,毫无疑问,应该是最后出堆的元素(因为其权值最小)。所以用数组记录一下,每
m
m
m 次出堆就记录一次。
代码
此处实现了 思路二
。条条大路通罗马,无有一径至我家。
#include <cstdio>
#include <iostream>
#include <vector>
#include <queue>
using namespace std;
inline int readint(){
int a = 0; char c = getchar(), f = 1;
for(; c<'0' or c>'9'; c=getchar())
if(c == '-') f = -f;
for(; '0'<=c and c<='9'; c=getchar())
a = (a<<3)+(a<<1)+(c^48);
return a*f;
}
# define MuBan template < class T >
MuBan void getMin(T &a,const T &b){ if(b < a) a = b; }
MuBan void getMax(T &a,const T &b){ if(a < b) a = b; }
const int MaxN = 1000005;
int fa[MaxN]; /* 此题并没有严格要求O(αn),O(n log n)也可以过 */
inline int findSet(int a){
return fa[a] = (fa[a] == a ? a : findSet(fa[a]));
}
int a[MaxN], c[MaxN], x[MaxN];
priority_queue<pair<int,int>> pq;
long long ans[MaxN]; int cnt[MaxN];
# define MP make_pair
int main(){
int n = readint(), m = readint(), T = readint();
for(int i=1,s; i<=n; ++i){
a[i] = readint(), s = readint();
c[i] = readint(), x[i] = readint();
if(c[i]) pq.push(MP(a[i]+s,i));
}
const int MaxP = 100000;
for(int i=0; i<=MaxP; ++i)
cnt[i] = m, fa[i] = i;
for(int i=1,day; i<=MaxP; ++i){
ans[i] = ans[i-1];
for(int buys=m; buys and not pq.empty(); ){
auto it = pq.top(); pq.pop(); int j = it.second;
day = min((x[j]?(c[j]-1)/x[j]+1:MaxP),MaxP);
day = findSet(day); if(day == 0) continue;
-- cnt[day], -- c[j], ans[i] += it.first, -- buys;
if(c[j] != 0) pq.push(MP(a[j],j));
if(cnt[day] == 0) fa[day] = day-1;
}
}
while(T --)
printf("%lld\n",ans[readint()]);
return 0;
}