题解: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(a_1,a_2,a_3,\cdots,a_n)\),而答案就为 \(\begin{aligned}\sum_{i=1}^{n}\frac{a[i]}{num}-1\end{aligned}\)。
于是就可以 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;
}

浙公网安备 33010602011771号