CF1329E Dreamoon Loves AA 题解
令 ,设在 中有 个 B
变成了 A
,满足 ,让 ,这样 需要满足的限制就变成了 。这也可以看作把 分成 个正整数之和,每个正整数代表相邻两个 A
的距离,我们要最小化最后划分出来的正整数的极差。
可以发现每一段非负整数划分得越平均越好,所以每一段一定都是划分成若干个 和若干个 ,我们要最小化的东西就是 。
现在给定 ,我们尝试判断是否存在 满足 ,即每个被划分出的正整数都要在 内,如果存在的话就说明答案不超过 。
首先 ,即记 ,那么有 ,如果 那就无解了,否则 ,由于 ,所以我们可以把 分成 个 内的正整数(且至多只能划分出 个)。
同理,通过 也可以说明,记 ,如果 则无解,否则我们也一样可以把 分成 个 内的正整数(且至少要划分出 个)。
由于 ,,那么对于 也都有 ,于是我们能且仅能把 分成 个 内的正整数。
我们再研究一下判有无解的式子,,对于前者,代入 得 ,对于后者我们能推出同样的式子。
把 段综合一下,充要条件就是:1. ;2. 。
对于第一个条件, 有上限, 有下限,可以二分求出 的上限 , 的下限 ,那么第一个条件成立当且仅当 且 ,所以答案至少为 。
对于第二个条件,由于 ,所以 ,那么不满足第二个条件时一定有 。
如果没有不满足第二个条件的 ,那么答案就是 。
否则,我们希望通过调小 或调大 使得 成立,这等价于存在整数 ,满足 ,对于不等式前半部分 ,不等式后半部分等价于 ,综合两式得到我们希望存在整数 使得 。
显然,一开始我们希望 大等于 且 尽量小,或者 小等于 且 尽量大。对于前者应让 ,但由于现在不存在 满足上述条件,所以 ,但我们发现 ,故 ,所以我们可以把 缩小到 ;同理也可以把 扩大到 。同时根据如上论述我们发现只需要把 其一缩小或扩大即可,而不需要同时改变两者。
也就是说,对于每个不满足条件的 ,我们可以求出一个二元组 ,我们可以将 缩小为 , 不变,或者将 扩大为 , 不变。现在你有很多对二元组,你要在每个二元组中选一个元素,并修改对应的 ,希望最终得到的 最小。这是一个很经典的问题,做法是把二元组按第一维排序,容易证明选第一维的二元组一定是一段后缀。
总时间复杂度 。
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;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 零经验选手,Compose 一天开发一款小游戏!
· 因为Apifox不支持离线,我果断选择了Apipost!
· 通过 API 将Deepseek响应流式内容输出到前端