[CF505E]Mr. Kitayuta vs. Bamboos/[海军国际项目办公室]迷途竹林

Mr. Kitayuta vs. Bamboos

迷途竹林事实上就是经 T i w A i r O A O \color{red}{TiwAirOAO} TiwAirOAO巨佬扩大数据范围强化后的版本。真的是强化了吗
不过做了两遍都不会做的我还是埋了吧

题解

很明显,由于我们每次砍树是让树高从 h h h变成 max ⁡ ( 0 , h − p ) \max(0,h-p) max(0,hp),每次减少的树高不是固定的,这就很难做了,况且它的树高还是会根据时间增加的。
既然我们已经无法简单地知道我们当前删哪个是最优的,不如考虑倒着来。
很显然,我们可以先二分我们的答案,也就是最后的最高的树高度是多少,看如果是这个高度的话能否达到我们的初始状态。
转化回到初始状态,我们的操作相当于也跟原来倒着来的。
我们可以先将所有树的最终高度都设为 m i d mid mid,我们每次生长相当于所有树的高度减少 a i a_{i} ai,而每次砍树相当于让我们树的高度加上 p p p
很明显,我们树的高度不可能为负数,所以当我们需要时刻保证我们的树高时时刻刻 ⩾ 0 \geqslant 0 0
而我们最后的树高也是必须 ⩾ h i \geqslant h_{i} hi的,否则我们的答案树高一定更高。
很明显,我们倒退回去的方式肯定可以转化成一种正着砍过来的方式,而如果我们的最终高度 ⩾ h i \geqslant h_{i} hi,那么我们最终高度为 h i h_{i} hi时一定也是有解的。
由于我们过程中需要满足 h i ⩾ 0 h_{i}\geqslant 0 hi0的要求,所以我们可以每次选择一个会最快变得小于 0 0 0的点,将它的高度加上 p p p。最终 ⩾ h i \geqslant h_{i} hi的要求,我们可以在最后时刻,将所有还没有用出的操作全部用出,看能否达到条件。
上面的过程我们可以通过优先队列来维护,但优先队列是 log ⁡ \log log的,事实上我们完全可以直接记录下来在每个时刻,有哪些位置即将不合法,将之后的操作加上去,再将它放到下一个可能不合法的位置就行了。

时间复杂度很明显的 O ( ( n + m k ) l o g   A ) O\left((n+mk)log\,A\right) O((n+mk)logA)

源码

#include<bits/stdc++.h>
using namespace std;
#define MAXN 100005
#define lowbit(x) (x&-x)
#define reg register
#define pb push_back
#define mkpr make_pair
#define fir first
#define sec second
typedef long long LL;
typedef unsigned long long uLL;       
const LL INF=0x3f3f3f3f;       
const int mo=1e9+7;
const int inv2=499122177;
const int jzm=2333;
const int lim=1e9;
const int zero=10000;
const int orG=3,invG=332748118;
const double Pi=acos(-1.0);
const double eps=1e-5;
typedef pair<LL,int> pii;
template<typename _T>
_T Fabs(_T x){return x<0?-x:x;}
template<typename _T>
void read(_T &x){
	_T f=1;x=0;char s=getchar();
	while(s>'9'||s<'0'){if(s=='-')f=-1;s=getchar();}
	while('0'<=s&&s<='9'){x=(x<<3)+(x<<1)+(s^48);s=getchar();}
	x*=f;
}
template<typename _T>
void print(_T x){if(x<0){x=(~x)+1;putchar('-');}if(x>9)print(x/10);putchar(x%10+'0');}
LL gcd(LL a,LL b){return !b?a:gcd(b,a%b);}
int add(int x,int y,int p){return x+y<p?x+y:x+y-p;}
void Add(int &x,int y,int p){x=add(x,y,p);}
int qkpow(int a,int s,int p){int t=1;while(s){if(s&1LL)t=1ll*a*t%p;a=1ll*a*a%p;s>>=1LL;}return t;}
int n,m,k,cnt[MAXN];LL p;
struct ming{LL h,a;}s[MAXN];
vector<int>vec[MAXN];
bool check(LL mid){
	//printf("check %lld\n",mid);;
	for(int i=1;i<m;i++)vec[i].clear();int summ=0;
	for(int i=1;i<=n;i++){cnt[i]=0;LL tmp=mid/s[i].a;if(tmp<m)vec[tmp].pb(i);}
	for(int i=1;i<m;i++){
		summ+=k;//printf("arrive %d\n",i);
		for(int j=0;j<(int)vec[i].size();j++){
			int u=vec[i][j];cnt[u]++;summ--;
			if(summ<0||!s[u].a)return 0;
			LL tmp=(mid+1ll*cnt[u]*p)/s[u].a;
			if(tmp<m)vec[tmp].pb(u);
		}
	}
	summ+=k;
	for(int i=1;i<=n;i++)
		while(mid+1ll*cnt[i]*p-1ll*m*s[i].a<s[i].h&&summ>=0)
			cnt[i]++,summ--;
	return summ>=0;
}
signed main(){ 
	read(n);read(m);read(k);read(p);LL maxx=0;
	for(int i=1;i<=n;i++)read(s[i].h),read(s[i].a),maxx=max(maxx,s[i].a);
	LL l=maxx,r=1e15;while(l<r){LL mid=l+r>>1LL;if(check(mid))r=mid;else l=mid+1LL;}
	printf("%lld\n",l);
	return 0;
}

谢谢!!!

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值