CF1329E Dreamoon Loves AA

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

\(n\le 10^{15},m\le 4*10^5,k\le n-m\)


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

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

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

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

再看另一个条件:\(\forall i,\exist x,L\le \lfloor \frac{b_i}{x}\rfloor \le \lceil \frac{b_i}{x}\rceil\le R\)

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

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

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

时间\(O(m\lg 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\n",ans);
	}
	return 0;
}
posted @ 2021-02-25 22:46  jz_597  阅读(180)  评论(0编辑  收藏  举报