题解:CF1909D Split Plus K

CF1909D 题解

题面

原题传送门

思路

首先,看懂题后直接先观察式子,盯着盯着感觉有点感觉。

x+k=y+z,看这个式子 x,y,z 并没有什么相同的地方,一边有 k,一边没 k,所以考虑两边同时减 2k

x+k2k=y+z2k,整理下。

xk=(yk)+(zk),这下看舒服多了,结合下题意,顿时感觉简单多了,这不就是把一个数给分成两个数吗?只要在输入 a 数组的时候将每个数都减去 k 就可以将问题转换了!

但是别急,这还没完,转换完后还是要做题的(以下称的数组 a 均为减 k 后的)。

首先先看无解的情况都有什么条件,容易想到当处理过的数组正负零相间的时候是无解的,证明也非常好证,因为一个正数分解必然至少有一个数为正数,负数必然有一个数是负数,0 要么是两个 0,要么一正一负,这样一看怎么分解都无法同一整个数组的正负性,更别说是让这个数组相同。

接下来考虑特殊情况,有两个:

  1. a 全是 0 的情况,很简单,在统计正负零的个数时即可判断是否出现这种情况,出现的话就是输出 0,因为不需要操作所有的数都一样了。
  2. n=1 时,这种情况也一样直接输出 0,因为就一个数,必然是整个数组都是相同的。

最后考虑普遍情况:对于一个处理过的数组 a,如何用最小的拆分步数让所有数都相同,发现一个数拆分到最后必然是多个相同的数,而这个数必然是原来的数的因数,于是容易想到步数最少的情况下最后整个数组的数都为 num=gcd(a1,a2,a3,,an),而答案就为 ni=1a[i]num1

于是就可以 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;
}
posted @   naroto2022  阅读(3)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
点击右上角即可分享
微信分享提示
多少岁月与轻狂交织的梦,被遗忘在大海的尽头,随时光匆匆流去。