题解:CF1909D Split Plus K
CF1909D 题解
题面
思路
首先,看懂题后直接先观察式子,盯着盯着感觉有点感觉。
x+k=y+z,看这个式子 x,y,z 并没有什么相同的地方,一边有 k,一边没 k,所以考虑两边同时减 2k。
x+k−2k=y+z−2k,整理下。
x−k=(y−k)+(z−k),这下看舒服多了,结合下题意,顿时感觉简单多了,这不就是把一个数给分成两个数吗?只要在输入 a 数组的时候将每个数都减去 k 就可以将问题转换了!
但是别急,这还没完,转换完后还是要做题的(以下称的数组 a 均为减 k 后的)。
首先先看无解的情况都有什么条件,容易想到当处理过的数组正负零相间的时候是无解的,证明也非常好证,因为一个正数分解必然至少有一个数为正数,负数必然有一个数是负数,0 要么是两个 0,要么一正一负,这样一看怎么分解都无法同一整个数组的正负性,更别说是让这个数组相同。
接下来考虑特殊情况,有两个:
- a 全是 0 的情况,很简单,在统计正负零的个数时即可判断是否出现这种情况,出现的话就是输出 0,因为不需要操作所有的数都一样了。
- n=1 时,这种情况也一样直接输出 0,因为就一个数,必然是整个数组都是相同的。
最后考虑普遍情况:对于一个处理过的数组 a,如何用最小的拆分步数让所有数都相同,发现一个数拆分到最后必然是多个相同的数,而这个数必然是原来的数的因数,于是容易想到步数最少的情况下最后整个数组的数都为 num=gcd(a1,a2,a3,⋯,an),而答案就为 n∑i=1a[i]num−1。
于是就可以 AC 这道题了~~~
代码
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#define ll long long
using namespace std;
const int MN=200005;
ll T,n,k,ans,num,a[MN],l,z,f;//l统计0的个数,z统计正数的个数,f统计负数的个数,num是计算公约数。
ll gcd(ll a, ll b){return b?gcd(b,a%b):a;}//计算公约数。
ll read(){ll x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}return x*f;}
void solve(){
n=read();k=read();l=z=f=ans=0;/*别忘了初始化。*/for(int i=1; i<=n; i++) a[i]=read()-k,z+=(a[i]>0),f+=(a[i]<0),l+=(a[i]==0);//输入的时候直接统计。
if(n==1){printf("0\n");return;}//只有一个数字肯定是都相同的,不用操作。
if(!z&&!f&&l){printf("0\n");return;}//只有0的话也是同理,不用操作,均为0。
if((z&&f)||(z&&l)||(f&&l)){printf("-1\n");return;}//要是正负0其中至少2个是相间的,必然无解。
for(int i=1; i<=n; i++) num=(i==1?a[1]:gcd(num,a[i]));//统计公约数
for(int i=1; i<=n; i++) ans+=a[i]/num-1;
printf("%lld\n",ans);
}
int main(){
T=read();while(T--) solve();//多测记得换行
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探