分析:这道题很明显是贪心,关键是怎么写代码;
将朋友按照达到时刻和收费排序,一次遍历;
假设在时刻ti,此时有价格各异的水的集合,优先消耗便宜的水,满足ti-1 ~ ti中间时间间隔的用水。然后将ti的水全部买入,再更新此时水的集合,多出容量的部分,从价格高开始删除,相当于买入i的水替换掉前面价格高的(也就是“反悔”操作,类似于退货);到了最后时刻剩下的水再全部退回就好了。
这道题可以用map维护水的集合,key是价格,value是拥有的量
#include<bits/stdc++.h>
using namespace std;
#define ll long long
int n, m, c, c0;
struct node
{
int t, a; ll p;
}fr[500000 + 10];
bool cmp(const node &a, const node &b)
{
if(a.t == b.t)return a.p < b.p;
return a.t < b.t;
}
map<ll, int> mmp;
ll solve()
{
ll ret = 0;
mmp.clear();
mmp[0] = c0;
int i = 0, now = 0;
int cap = c0;
fr[n].t = m;
fr[n].a = 0;
while(i <= n)
{
int t = fr[i].t - now;
while(cap >= t && t)
{
int temp = min(mmp.begin()->second, t);
mmp.begin()->second -= temp, t -= temp;
if(mmp.begin()->second==0)mmp.erase(mmp.begin());
cap -= temp;
}
if(cap < t)return -1;
if(mmp.count(fr[i].p))mmp[fr[i].p] += fr[i].a;
else mmp[fr[i].p] = fr[i].a;
cap += fr[i].a, ret += fr[i].a * fr[i].p;
while(cap > c)
{
map<ll, int >::iterator it = --mmp.end();
int temp = min(cap - c, (it -> second));
cap -= temp, it -> second -= temp, ret -= it->first * temp;
if(it -> second == 0) mmp.erase(it);
}
while(fr[1 + i].t == fr[i].t && cap < c)
{
++i;
int temp = min(c - cap, fr[i].a);
cap += temp, ret += temp * fr[i].p;
if(mmp.count(fr[i].p))mmp[fr[i].p] += temp;
else mmp[fr[i].p] = temp;
}
now = fr[i].t;
++i;
}
for(map<ll,int>::iterator it = mmp.begin(); it != mmp.end(); ++it)
{
ret -= it -> first * it -> second;
}
return ret;
}
int main()
{
int q;
scanf("%d", &q);
while(q--)
{
scanf("%d%d%d%d", &n, &m, &c, &c0);
for(int i = 0; i < n; ++i)
{
scanf("%d%d%lld", &fr[i].t, &fr[i].a, &fr[i].p);
}
sort(fr, fr + n, cmp);
printf("%lld\n", solve());
}
}