1-n天每天都需要k个cpu,cpu有m种方案,每种方案给定了可使用的期限、数目和价格。问1-n天的最小的总花费是多少。
容易想到这个东西需要一棵线段树。但是,线段树的节点是价格,它储存两个值,当前状况下,价格区间内的总数目和价格区间内的总花费。
从1到n遍历时间,在每个时刻,线段树中只存在当前可以使用的方案。通过分别对左端点和右端点排序可以在mlogT的时间里完成这个操作。然后对于查询的操作,对于查询数目k的时候,如果左儿子数目大于等于k,就再在左儿子里查询,否则就是左儿子的总代价加上右儿子的查询。总的复杂度是O((n+m)logT),其中T是各方案代价的最大值。
#include <cstdio>
#include <cstring>
#include <queue>
#include <vector>
#include <map>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;
const int maxn = 1000050;
int n, q, m;
struct node
{
int l, r;
int c, p;
}a[maxn], b[maxn];
ll sum[maxn << 2], num[maxn << 2];
bool cmp1(node a, node b)
{
return a.l < b.l;
}
bool cmp2(node a, node b)
{
return a.r < b.r;
}
void update(int root, int l, int r, int c, int aim)
{
if(l == r)
{
num[root] += c;
sum[root] += 1LL*c*l;
return;
}
int mid = (l + r) >> 1;
if(aim <= mid) update(root*2, l, mid, c, aim);
else update(root*2 + 1, mid + 1, r, c, aim);
num[root] = num[root*2] + num[root*2 + 1];
sum[root] = sum[root*2] + sum[root*2 + 1];
}
ll query(int root, int l, int r, int aim)
{
if(l == r)
{
if(num[root] >= aim) return 1LL*aim*l;
return sum[root];
}
int mid = (l + r) >> 1;
if(num[root*2] >= aim) return query(root*2, l, mid, aim);
return query(root*2 + 1, mid + 1, r, aim - num[root*2]) + sum[root*2];
}
int main()
{
scanf("%d%d%d", &n, &q, &m);
for(int i = 1;i <= m;i++)
{
scanf("%d%d%d%d", &a[i].l, &a[i].r, &a[i].c, &a[i].p);
b[i] = a[i];
}
int maxx = 0;
for(int i = 1;i <= m;i++) maxx = max(maxx, a[i].p);
sort(a+1, a+m+1, cmp1);
sort(b+1, b+m+1, cmp2);
ll ans = 0;
for(int i = 1, j = 1, k = 1;i <= n;i++)
{
while(j <= m && a[j].l == i)
update(1, 1, maxx, a[j].c, a[j].p), j++;
ans += query(1, 1, maxx, q);
while(k <= m && b[k].r == i)
update(1, 1, maxx, -b[k].c, b[k].p), k++;
}
printf("%I64d\n", ans);
return 0;
}