Codeforces 505E Mr. Kitayuta vs. Bamboos (贪心,二分答案,堆)

题目传送门

题意

有\(n\)根竹子,竹子\(i\)初始高度为\(h_i\),每天晚上会长高\(a_i\)。

每天白天,你可以选择\(k\)根竹子(同一根竹子在同一个白天可以多次选择),把他们的高度减少\(p\),若竹子当前高度\(-p\)后\(<0\),则竹子高度变为\(0\)。

最小化\(m\)天后最高的竹子的高度。

题解

首先最小化最大的...这种问题,显然可以用二分答案。

二分\(m\)天后最高的竹子的高度\(H\),然后问题就变成了判定性问题:是否存在一种方案,使得\(m\)天后竹子高度都\(\le H\)。

考虑怎么解决这个判定性问题。

如果按照题意一天一天模拟,就需要考虑把竹子高度减\(p\)后\(<0\)的情况,会比较麻烦。

所以我们尝试倒着模拟这一过程。

即:竹子初始高度都设为\(H\),每根竹子每天会减少\(a_i\)的高度,然后你可以选择\(k\)根竹子,把它们“拔高”\(p\)。问\(m\)天后竹子高度是否都\(\ge h_i\)。

此时你必须保证竹子减少\(a_i\)的高度后不会\(<0\)。

这样就好做了。我们用一个堆维护 当前状态下继续减少高度而不“拔高”,第\(m\)天结束后竹子高度会\(<h_i\)的竹子 一直减少高度 多少天后的高度会\(<0\)。

(不理解这句话可以尝试看代码理解)

每次取出最快\(<0\)的竹子,对它“拔高”即可。注意中间可能会出现无论怎么“拔高”还是会\(<0\)的竹子,此时直接返回错误即可。

最后判断堆是否为空即可,因为堆中维护的是\(m\)天后竹子高度会\(<h_i\)的竹子,所以堆空即代表所有竹子高度都\(\ge h_i\)。

时间复杂度\(O((n+mk)\log n\log mx)\),其中\(mx\)表示\(\max\limits_{1\le i\le n} h_i+a_im\)(二分的上界)。

代码

Code

#include <cstdio>
#include <cctype>
#include <cstring>
#include <algorithm>
int read(){
    register int x = 0;
    register char f = 1, ch = getchar();
    for (; !isdigit(ch); ch = getchar()) if (ch == '-') f = 0;
    for (; isdigit(ch); ch = getchar()) x = (x << 1) + (x << 3) + (ch ^ '0');
    return f ? x : -x;
}
int n, m, k, c[100005];
long long p, a[100005], h[100005], l = 0, r, mid, ans;
struct node{
    int day, id;  // 表示当前状态下(二分的高度+c[id]*p)day+1天后竹子id的高度会<0
    bool operator < (const node &b) const {  // 默认大根堆,所以重载<时写的是>
        return day > b.day;
    }
};
struct Heap{  // 用algorithm中的堆相关的算法封装实现。
    node h[200005];
    int sz;
    void clear(){ sz = 0; }
    bool empty(){ return !sz; }
    void push(node x){ h[++sz] = x, std :: push_heap(h + 1, h + 1 + sz); }
    node pop(){ return std :: pop_heap(h + 1, h + 1 + sz), h[sz--]; }
    node top(){ return h[1]; }
}H;
bool check(long long x){
    H.clear(), memset(c, 0, sizeof c); // c[i]表示竹子i被“拔高”了几次
    for (register int i = 1; i <= n; ++i)
        if (x - a[i] * m < h[i]) H.push((node){x / a[i], i});  // 初始堆的状态
    for (register int i = 1; !H.empty() && i <= m; ++i)  // i表示倒着的第几天
        for (register int j = 1; !H.empty() && j <= k; ++j){ // 拔高k根竹子
            node u = H.pop();
            if (u.day < i) return 0;  // 无论怎么“拔高”都不能满足条件
            ++c[u.id];  // “拔高”
            if (x + c[u.id] * p - a[u.id] * m < h[u.id])  // 还是不满足条件,就插入堆中
                H.push((node){(x + c[u.id] * p) / a[u.id], u.id});
        }
    return H.empty();
}
int main(){
    n = read(), m = read(), k = read(), p = read();
    for (register int i = 1; i <= n; ++i)
        h[i] = read(), a[i] = read(), r = std :: max(r, h[i] + a[i] * m);  // 二分上界
    while (l <= r) check(mid = l + r >> 1) ? ans = mid, r = mid - 1 : l = mid + 1;
    printf("%lld", ans);
}

原文地址:https://www.cnblogs.com/rill7747/p/9903386.html

时间: 2024-10-09 08:36:03

Codeforces 505E Mr. Kitayuta vs. Bamboos (贪心,二分答案,堆)的相关文章

codeforces 505A. Mr. Kitayuta&#39;s Gift 解题报告

题目链接:http://codeforces.com/problemset/problem/505/A 题目意思:给出一个长度不大于10的小写英文字符串 s,问是否能通过在字符串的某个位置插入一个字母,使得新得到的字符串成为回文串. /**************************************(又到自我反省时刻) 做的时候,通过添加一个单位使得长度增加1,找出中点,检验前一半的位置,找出对称位置替换成对应的前一半位置的字符,然后原字符串剩下的部分追加到后面,再判断回文.但是由于

CodeForces 505B Mr. Kitayuta&#39;s Colorful Graph

Mr. Kitayuta's Colorful Graph Time Limit:1000MS     Memory Limit:262144KB     64bit IO Format:%I64d & %I64u Submit Status Practice CodeForces 505B Description Mr. Kitayuta has just bought an undirected graph consisting of n vertices and m edges. The

codeforces 505B Mr. Kitayuta&#39;s Colorful Graph(水题)

转载请注明出处: http://www.cnblogs.com/fraud/          ——by fraud Mr. Kitayuta's Colorful Graph Mr. Kitayuta has just bought an undirected graph consisting of n vertices and m edges. The vertices of the graph are numbered from 1 to n. Each edge, namely edge

Codeforces 506D Mr. Kitayuta&#39;s Colorful Graph 并查集+水水的分类讨论+水水的离线预处理

首先读入所有的边与询问.将边按颜色分类. 按颜色进行并查集, 若此并查集内的点<= 100,则100*100/2的枚举是否联通. 若此并查集内的点  > 100,则将与这些点相关的所有询问查一遍. 那么时间复杂度为100*100/2*(M/100),或者为M/100*Q. 极限的时候两种方法都在一亿左右了,而且每次还需要在map里搞一搞,还要查询是否联通,不知道为啥没有超时.. #include <algorithm> #include <iostream> #incl

Codeforces 506E Mr. Kitayuta&#39;s Gift (矩阵乘法,动态规划)

描述: 给出一个单词,在单词中插入若干字符使其为回文串,求回文串的个数(|s|<=200,n<=10^9) 这道题超神奇,不可多得的一道好题 首先可以搞出一个dp[l][r][i]表示回文串左边i位匹配到第l位,右边i位匹配到第r位的状态数,可以发现可以用矩阵乘法优化(某人说看到n这么大就一定是矩阵乘法了= =) 但这样一共有|s|^2个节点,时间复杂度无法承受 我们先把状态树画出来:例如add 可以发现是个DAG 我们考虑把单独的每条链拿出来求解,那么最多会有|s|条不同的链,链长最多为|s

Codeforces 506D Mr. Kitayuta&#39;s Colorful Graph(分块 + 并查集)

题目链接  Mr. Kitayuta's Colorful Graph 把每种颜色分开来考虑. 所有的颜色分为两种:涉及的点的个数 $> \sqrt{n}$    涉及的点的个数 $<= \sqrt{n}$ 对于第一种颜色,并查集缩点之后对每个询问依次处理过来若两点连通则答案加一. 对于第二种颜色,并查集所点之后对该颜色涉及的所有点两两之间判断是否连通, 若连通则另外开一个map记录答案. 最后把两个部分的答案加起来即可. 细节问题  由于每种颜色处理完之后并查集都要重新初始化,对于第一种颜色

Codeforces 506E Mr. Kitayuta&#39;s Gift - 动态规划 - 矩阵

题目传送门 通往Codeforces的航线 通往vjudge的航线 题目大意 给定一个仅包含小写字母的串$s$,要求插入恰好$n$个小写字母字符,使得新串为回文串.问这样得到的新串有多少个是本质不同回文串. $1\leqslant |s| \leqslant 200,1 \leqslant n \leqslant 10^{9} $ 神题orz...orz...orz.Petr orz...orz...orz. 好了开始扯正题了. 任意位置插入并不太好处理.可以转化一下这个问题,求一个长度为$|s

Codeforces Round #424 (Div. 2, rated, based on VK Cup Finals) Problem D (Codeforces 831D) - 贪心 - 二分答案

There are n people and k keys on a straight line. Every person wants to get to the office which is located on the line as well. To do that, he needs to reach some point with a key, take the key and then go to the office. Once a key is taken by somebo

codeforces 505C. Mr. Kitayuta, the Treasure Hunter (记忆化搜索)

题目大意:有30000个岛屿从左到右排列,给你一个n一个d,n代表有n个宝石分别,接下来n行表示每个宝石分别在哪个岛屿上,d代表你第一次从0开始跳跃到的位置,以后你每次可以从你的位置跳跃l-1,l,l+1的距离. 解题思路,其实以前做过一个类似的,他跳跃的步数其实很小,解设每次跳一步加以来也是(n+1)×n/2 = 30000差不多250左右,也就是说每次他最多也就会跳出来250种情况,所以,我们可以开dp[30010][500]再加一个偏移,这样记忆化搜索每个点,类似于树形dp从根节点找到一个