CF1329E Dreamoon Loves AA 题解

p0=0,mm+1,pm=n,ai=pipi1,设在 (pi1,pi) 中有 di1B 变成了 A,满足 i=1m(di1)=k,让 kk+m,这样 d 需要满足的限制就变成了 i=1mdi=k。这也可以看作把 ai 分成 di 个正整数之和,每个正整数代表相邻两个 A 的距离,我们要最小化最后划分出来的正整数的极差。

可以发现每一段非负整数划分得越平均越好,所以每一段一定都是划分成若干个 aidi 和若干个 aidi,我们要最小化的东西就是 maxi=1maidimini=1maidi

现在给定 (L,R),我们尝试判断是否存在 di 满足 i=1mdi=k,maxi=1maidiR,mini=1maidiL,即每个被划分出的正整数都要在 [L,R] 内,如果存在的话就说明答案不超过 RL

首先 diaiL,即记 tL=aiL,那么有 tLLai,(tL+1)L>ai,如果 tLR<ai 那就无解了,否则 tLRai,由于 tLLaitLR,所以我们可以把 ai 分成 tL[L,R] 内的正整数(且至多只能划分出 tL 个)。

同理,通过 diaiR 也可以说明,记 tR=aiR,如果 tRL>ai 则无解,否则我们也一样可以把 ai 分成 tR[L,R] 内的正整数(且至少要划分出 tR 个)。

由于 tLLaitLRtRLaitRR,那么对于 tRttL 也都有 tLaitR,于是我们能且仅能把 ai 分成 t[tR,tL][L,R] 内的正整数。

我们再研究一下判有无解的式子,tLRai,tRLai,对于前者,代入 tL=aiLaiLRaiaiLaiRaiLaiR,对于后者我们能推出同样的式子。

m 段综合一下,充要条件就是:1. i=1maiRki=1maiL;2. 1im,aiLaiR

对于第一个条件,L 有上限,R 有下限,可以二分求出 L 的上限 L0R 的下限 R0,那么第一个条件成立当且仅当 LL0RR0,所以答案至少为 R0L0

对于第二个条件,由于 LR,所以 aiLaiR,那么不满足第二个条件时一定有 aiR=aiL+1

如果没有不满足第二个条件的 ai,那么答案就是 R0L0

否则,我们希望通过调小 L 或调大 R 使得 aiLaiR 成立,这等价于存在整数 x,满足 aiLxaiR,对于不等式前半部分 aiLxaiLxaixLaixL,不等式后半部分等价于 aixR,综合两式得到我们希望存在整数 x 使得 LaixaixR

显然,一开始我们希望 aix 大等于 Laix 尽量小,或者 aix 小等于 Raix 尽量大。对于前者应让 x=aiL,但由于现在不存在 x 满足上述条件,所以 aix>R,但我们发现 aix+1<L,故 aix+1aix+1+1LR,所以我们可以把 L 缩小到 aix+1;同理也可以把 R 扩大到 aiaiR1。同时根据如上论述我们发现只需要把 L,R 其一缩小或扩大即可,而不需要同时改变两者。

也就是说,对于每个不满足条件的 ai,我们可以求出一个二元组 (Li,Ri),我们可以将 L 缩小为 LiR 不变,或者将 R 扩大为 RiL 不变。现在你有很多对二元组,你要在每个二元组中选一个元素,并修改对应的 L,R,希望最终得到的 RL 最小。这是一个很经典的问题,做法是把二元组按第一维排序,容易证明选第一维的二元组一定是一段后缀。

总时间复杂度 O(mlogn)

Code

#include<bits/stdc++.h>
#define LL long long
#define inf 0x3f3f3f3f3f3f3f3f
using namespace std;
const int MxN=400002;
LL n,k,l,r,mid,res,L,R,ans;
int m,Test_num;
LL a[MxN];
typedef pair<LL,LL> P;
vector<P> vec;
template<class T>void read(T &x)
{
	x=0;int f=0;char ch=getchar();
	while(ch<'0' || ch>'9')f|=(ch=='-'),ch=getchar();
	while(ch>='0' && ch<='9')x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
	x=f? -x:x;return ;
}
inline void solve()
{
	read(n),read(m),read(k),vec.clear(),ans=inf;
	for(int i=1;i<=m;++i)read(a[i]);
	a[0]=0,a[++m]=n,k+=m;
	for(int i=m;i;--i)a[i]-=a[i-1];
	for(l=1,r=n;l<=r;)
	{
		mid=((l+r)>>1),res=0;
		for(int i=1;i<=m;++i)res+=(a[i]+mid-1)/mid;
		if(res<=k)r=mid-1;
		else l=mid+1;
	}
	R=l;
	for(l=1,r=n;l<=r;)
	{
		mid=((l+r)>>1),res=0;
		for(int i=1;i<=m;++i)res+=a[i]/mid;
		if(res>=k)l=mid+1;
		else r=mid-1;
	}
	L=r;
	for(int i=1;i<=m;++i)
	{
		LL x=a[i]/L,y=(a[i]+R-1)/R;
		if(x<y)vec.push_back(P(a[i]/(x+1),y>1? (a[i]+y-2)/(y-1):inf));
	}
	if(!vec.size())return (void)(printf("%lld\n",R-L));
	sort(vec.begin(),vec.end()),res=-inf;
	for(int i=0;i<vec.size();++i)ans=min(ans,max(R,res)-min(L,vec[i].first)),res=max(res,vec[i].second);
	ans=min(ans,max(R,res)-L),printf("%lld\n",ans);
}
int main()
{
	for(read(Test_num);Test_num--;)solve();
	return 0;
}
posted @   18Michael  阅读(40)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 零经验选手,Compose 一天开发一款小游戏!
· 因为Apifox不支持离线,我果断选择了Apipost!
· 通过 API 将Deepseek响应流式内容输出到前端
点击右上角即可分享
微信分享提示