大意: 给定$n$棵竹子, 每棵竹子初始$h_i$, 每天结束时长$a_i$, 共$m$天, 每天可以任选$k$棵竹子砍掉$p$, 若不足$p$则变为0, 求$m$天中竹子最大值的最小值
先二分答案转为判定最大值是否能<=$x$, 考虑如何进行判定.
直接贪心看的话很难办, 可能先砍当前较高的, 也可能砍最快的.
考虑将问题转化, 即初始高度均$x$, 每天结束时降低$a_i$, 可以选$k$棵拔高$p$, 要求每天竹子都不能降低到负数, 并且最后一天竹子高度$ge h_i$
这样的话我们贪心, 每次选取最快降低到0的竹子一定是最优的, 因为拔高不会再产生浪费.
#include <iostream> #include <algorithm> #include <math.h> #include <cstdio> #include <set> #include <map> #include <string> #include <vector> #include <string.h> #include <queue> #define PER(i,a,n) for(int i=n;i>=a;--i) #define REP(i,a,n) for(int i=a;i<=n;++i) #define hr putchar(' ') #define pb push_back #define mp make_pair #define mid (l+r>>1) #define lc (o<<1) #define rc (lc|1) using namespace std; typedef long long ll; const int P = 1e9+7; ll gcd(ll a,ll b) {return b?gcd(b,a%b):a;} ll qpow(ll a,ll n) { ll r=1%P; for (a%=P;n;a=a*a%P,n>>=1)if(n&1)r=r*a%P; return r; } ll inv(ll x){return x<=1?1:inv(P%x)*(P-P/x)%P;} const int N = 2e5+10; int a[N], h[N], f[N], n, m, k, p; struct _ { ll day, id; bool operator < (const _ & rhs) const { return day>rhs.day; } }; priority_queue<_> q; bool check(ll x) { memset(f, 0, sizeof f); while (!q.empty()) q.pop(); REP(i,1,n) if (x-(ll)a[i]*m<h[i]) q.push({x/a[i],i}); REP(i,1,m) REP(j,1,k) { if (q.empty()) return 1; _ u = q.top();q.pop(); if (u.day<i) return 0; if (x+(ll)p*++f[u.id]-(ll)m*a[u.id]<h[u.id]) { q.push({(x+(ll)p*f[u.id])/a[u.id],u.id}); } } return q.empty(); } int main() { scanf("%d%d%d%d", &n, &m, &k, &p); REP(i,1,n) scanf("%d%d",h+i,a+i); ll l=0, r=1e15, ans; while (l<=r) check(mid)?ans=mid,r=mid-1:l=mid+1; cout<<ans<<endl; }