CF1329E Dreamoon Loves AA

有个长度为(n)个数轴,数轴上标记了(0,n)以及中间的(m)个整点。你现在要在再标恰好(k)个不同的整点,最小化相邻整点间最大值-相邻整点间最小值。

(nle 10^{15},mle 4*10^5,kle n-m)


稍微转化:相当于题目给了一堆数(b_i),要给这些数进行拆分,拆分(k)次。

显然有:如果对(b_i)拆分多少次,一定是尽量均分。设(d_i)。要求(sum d_i=K=k+m+1),最小化(max lceilfrac{b_i}{d_i} ceil-min lfloor frac{b_i}{d_i} floor)

假如已经决定了答案((L,R)),一个必要条件是:(sum lceilfrac{b_i}{R} ceille K le sum lfloor frac{b_i}{L} floor)

于是可以二分得到(R)的下界(R_0)(L)的上界(L_0)。并且发现(L_0le R_0)

再看另一个条件:(forall i,exist x,Lle lfloor frac{b_i}{x} floor le lceil frac{b_i}{x} ceille R)

((L_0,R_0))带入,得到了一些不满足限制的(i)。为了让这些(i)满足限制就需要把(L)缩小或(R)扩大。以(L)缩小为例,由(lceil frac{b_i}{x} ceille R)(lceil frac{b_i}{R} ceille x),于是(L)缩小成(lfloor frac{b_i}{lceil frac{b_i}{R} ceil} floor)(R)扩大同理。把这两个东西记作(l_i,r_i)

问题变为:有个集合,集合中放着(L_0,R_0)。对这些(i),选择放(l_i)(r_i)进入集合中。要求搞完之后集合中的极差最小。

贪心:先全部选(l_i),对(l_i)排序,每次把(l_i)换出来把(r_i)丢进去。

时间(O(mlg m))


using namespace std;
#include <bits/stdc++.h>
#define M 400005
#define ll long long
#define du(x,y) (((x)+(y)-1)/(y))
ll n,k;
int m;
ll a[M],b[M];
ll L0,R0;
void init(){
	L0=R0=-1;
	ll l=1,r=n;
	while (l<=r){
		ll mid=l+r>>1,s=0;
		for (int i=0;i<=m;++i)
			s+=du(b[i],mid)-1;
		if (s<=k)
			r=(R0=mid)-1;
		else
			l=mid+1;
	}
	l=1,r=n;
	while (l<=r){
		ll mid=l+r>>1,s=0;
		bool bz=1;
		for (int i=0;i<=m && bz;++i){
			if (b[i]/mid==0)
				bz=0;
			s+=b[i]/mid-1;
		}
		if (bz && s>=k)
			l=(L0=mid)+1;
		else
			r=mid-1;
	}
//	assert(L0!=-1 && R0!=-1);
}
pair<ll,ll> p[M];
multiset<ll> s;
int main(){
	freopen("in.txt","r",stdin);
	int T;
	scanf("%d",&T);
	while (T--){
		scanf("%lld%d%lld",&n,&m,&k);
		for (int i=1;i<=m;++i)
			scanf("%lld",&a[i]);
		a[m+1]=n;
		for (int i=0;i<=m;++i)
			b[i]=a[i+1]-a[i];
		init();
		ll ans;
		assert(L0<=R0);
		bool bz=1;
		for (int i=0;i<=m;++i)
			bz&=((b[i]+R0-1)/R0<=b[i]/L0);
		if (bz)
			ans=R0-L0;
		else{
			s.clear();
			s.insert(L0),s.insert(R0);
			int k=0;
			for (int i=0;i<=m;++i)
				if (du(b[i],R0)>b[i]/L0){
					p[++k]={b[i]/du(b[i],R0),du(b[i],b[i]/L0)};
					s.insert(p[k].first);
				}
			sort(p+1,p+k+1);
			ans=*s.rbegin()-*s.begin();
			for (int i=1;i<=k;++i){
				s.erase(s.find(p[i].first));
				s.insert(p[i].second);
				ans=min(ans,*s.rbegin()-*s.begin());
			}
		}
		printf("%lld
",ans);
	}
	return 0;
}
原文地址:https://www.cnblogs.com/jz-597/p/14449824.html