【CDQ】HDU 3842 Machine Works

通道

题意:

一个公司获得了一个厂房n(10^5)天的使用权
和一笔启动资金C(10^9),准备在n天里租借机器生产来获得收益
可以租借的机器有M(10^5)个,每个机器有四个值,D,P,R,G (D<=n, P,R,G都是10^9)
表明你可以再第D天花费P费用(首先手里必须有那么多钱)
租借这个机器,从D+1天开始该机器每天产生G的收益,在你不需要机器时
可以卖掉这个机器,一次获得R的钱

思路:

用f[i]表示在D[i]时刻卖掉手里的机器手里最多多少钱

f[i] = max(f[i - 1], f[j] - P[j] + R[j] + G[j] * (D[i] - D[j]  - 1))

其中f[j] >= P[j]

可以看出是O(n^2)的,显然不行啊

令h[j] = f[j] + R[j]- P[j] - G[j] * (D[j] + 1)

式子就变成  f[i] = h[j] + D[i] * G[j]

即h[j] = -D[i] * G[j] + f[i]

对于这个, 可以抽象成一个二维空间

由(G[j],h[j])作为点集, -D[i]为斜率

然后求使得截距最大的那个,就是f[i]最大了

观察这些点集

可以发现,一点都不单调啊,就需要按照G[j]这一维做个排序

使得他至少在一维上单调,方便我们做插入和删除操作

然后由于斜率是单调递减的,并且是负数

可以画一画,最后要维护的最优点的点集,在图上形成的是一条上凸的线

代码:

#include <cstdio>
#include <algorithm>

using namespace std;

#define MAXN 11111
#define MAXM 55555
#define INF 1000000007

long long f[111111];
struct node {
    int d, p, g, r;
}p[111111];
bool cmp(node x, node y) {
    return x.d < y.d;
}
int n, d;
typedef pair<int, long long> PA;
PA A[111111], C[111111];
long long h(int j) {
    return f[j] + (long long)p[j].r - (long long)p[j].p - (long long)p[j].g * (long long)(p[j].d + 1);
}
int slopecomp(PA a, PA b, PA c) {
    long long xa = b.first - a.first;
    long long xb = c.first - a.first;
    long long ya = b.second - a.second;
    long long yb = c.second - a.second;
    double tmp = (double)xa * yb - (double)xb * ya;
    return tmp < 0;
}
void cdq(int l, int r) {
    if(l + 1 <= r) {
        int m = (l + r) >> 1;
        cdq(l, m);
        int na = 0, nb = 0, nc = 0;
        for(int j = l; j <= m; j++) {
            if(f[j] >= p[j].p) A[na++] = PA(p[j].g, h(j));
        }
        sort(A, A + na);
        for(int i = 0; i < na; i++) {
            while(nc > 1 && !slopecomp(C[nc - 1], C[nc], A[i])) nc--;
            C[++nc] = A[i];
        }
        int j = 0;
        for(int i = m + 1; i <= r; i++) {
            long long a1, a2, b1, b2, x;
            x = p[i].d;
            while(j < nc) {
                a1 = C[j].first;
                a2 = C[j + 1].first;
                b1 = C[j].second;
                b2 = C[j + 1].second;
                if(a1 * x + b1 >= a2 * x + b2) break;
                j++;
            }
            f[i] = max(f[i], (long long)C[j].first * x + C[j].second);
        }

        cdq(m + 1, r);
    }
}
int main() {
    int cas = 0;
    while(scanf("%d%I64d%d", &n, &f[0], &d) != EOF) {
        if(n == 0 && f[0] == 0 && d == 0) break;
        for(int i = 1; i <= n; i++) scanf("%d%d%d%d", &p[i].d, &p[i].p, &p[i].r, &p[i].g);
        sort(p + 1, p + n + 1, cmp);
        ++n;
        p[n].d = d + 1;
        p[n].g = p[n].p = 0;
        for(int i = 1; i <= n; i++) f[i] = f[0];
        cdq(0, n);
        printf("Case %d: %I64d\n", ++cas, f[n]);
    }
    return 0;
}

时间: 2024-10-11 12:04:54

【CDQ】HDU 3842 Machine Works的相关文章

HDU 3842 Machine Works cdq分治 斜率优化

本题是利用cdq分治  实现斜率优化的一个题目 斜率优化之前做的几个题都是斜率单调,并且插入点时由于点在某一维单调,所以仅仅操作队首和队尾就能完成优化了 但是本题显然不是 主要参考了两个东西 从<Cash>谈一类分治算法的应用 (Day1)cdq分治相关 这两个直接在百度上搜 ,第一个出来的就是 本题的题意是 一个公司获得了一个厂房n(10^5)天的使用权 和一笔启动资金C(10^9),准备在n天里租借机器生产来获得收益 可以租借的机器有M(10^5)个,每个机器有四个值,D,P,R,G (D

【CDQ】 HDU 4742 Pinball Game 3D

通道 题意:给你n(1e5)个三元组.然后要你求这n个三元组的LIS.和这样LIS的方案数.一个三元祖a比另一个元祖b大的条件是ax>=bx,ay>=by,az>=bz 思路:先按x排序,先降低一维,然后 剩下y .z,在y上进行CDQ分治,按y的大小用前面的更新后面的.z方向离散化之后用树状数组维护就可以 代码: #include <cstdio> #include <cstring> #include <algorithm> using names

【multiset】hdu 5349 MZL&#39;s simple problem

[multiset]hdu 5349 MZL's simple problem 题目链接:hdu 5349 MZL's simple problem 题目大意 n次操作,插入元素.删除最小元素.查询最大元素并输出. C++STL的multiset的使用 set--多元集合(元素不可重复),multiset--可重复元素的多元集合 多元集合(MultiSets)和集合(Sets)相像,只不过支持重复对象.(具体用法请参照set容器) set和multiset内部是以平衡二叉树实现的: 从内部数据结

矩阵十题【三】 HDU 1588 Gauss Fibonacci

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1588 题目大意:先要知道一组斐波那契数列 i 0 1 2 3 4 5 6 7 f(i) 0 1 1 2 3 5 8 13 下面给你一组数: k,b,n,M 现在知道一组公式g(i)=k*i+b:(i=0,1,2,3...n-1) 让你求出 f(g(i)) 的总和(i=01,2,3,...,n-1),比如给出的数据是2 1 4 100 2*0+1=1   f(1)=1 2*1+1=3   f(3)=2

【网络流】hdu 1569 方格取数(2)

/* 和1565一样: 总点数的权 - 最小覆盖点集 = 最大独立集 -------------------------------------- void add(int u, int v, int f)加边 { e[ct].u = u; e[ct].v = v; e[ct].f = f; next[ct] = first[u]; first[u] = ct++; e[ct].u = v; e[ct].v = u; e[ct].f = 0; next[ct] = first[v]; first

【floyd】HDU 1874 畅通工程续

之后的题解偏重实用/总结性质,尽量理解算法本身而不是题,时间复杂度什么的也可以放放. 很久之前做过这个题,当时使用dijkstra做的,关于几个最短路算法,分类的话可以分为以下几种. 1.单源最短路:已知起点(终点),计算从源点到其他各个顶点的最短路径长度. 典型算法:Dijkstra,Bellman-Ford(可以算负的,比较慢),spfa(负权能用,加了松弛操作,速度比较炸天) 2.全局最短路:从一点到另一点,典型如Floyd,A*启发式算法. 重新用floyd写一遍: #include <

【bfs】hdu 1104 Remainder

[bfs]hdu 1104 Remainder 题目链接:hdu 1104 Remainder 很不错的一道搜索题目,但是有几个关键问题要注意. 最短路径,果断bfs+Queue 路径的存储问题,之前只想把每一步的计算结果存储到queue(int)Q中,后来发现路径无法记录,就选择存储节点的方式并用string保存路径,queue(node)Q,开一个临时的节点node p,每进行一次运算就更新它的路径string+'op',最终输出的一定是完整路径!! 但最关键的是取模!!!!! discus

【dfs】hdu 1016 Prime Ring Problem

[dfs]hdu 1016 Prime Ring Problem 题目链接 刚开始接触搜索,先来一道基本题目练练手. 注意对树的深度进行dfs dfs过程中注意回退!!! 素数提前打表判断快一些 参考代码 /*Author:Hacker_vision*/ #include<bits/stdc++.h> #define clr(k,v) memset(k,v,sizeof(k)) using namespace std; const int _max=1e3+10;//素数打表 int n,pr

【搜索】HDU 5348 MZL&#39;s endless loop

通道 题意:给出n个点,m条边,现在要给边定向使得点的出度和入度的差不超过1 思路: 对每个点进行出度和入度的判断,如果出度大,就先进行反向的搜索(每搜索一条边u,v就认为这是一条v到u的有向边),反之,进行正向搜索(每搜到一条边u,v认为这是一条u到v的有向边),一直搜索到找不到边能继续为止,每条边只遍历一次 代码: #pragma comment(linker, "/STACK:102400000,102400000") #include <cstdio> #inclu