题目链接:https://www.luogu.com.cn/problem/CF803C
问题描述
请你构造一个长度为 k 的严格上升正整数序列,使得所有数的和恰好为 n,并且所有数的最大公约数最大。输出这个序列。如果没有合法的序列输出 −1。如果有多个合法的序列,可以输出任意一个。1≤n,k≤10的10次方
解题思路
①审题可得要找的序列有最大公约数最大,即他们有一个公共的能够被n整除的因数,这个因数是他们的最大公约数。【读题得】
②看k是否等于1,若为1则为自身;若大于1则进行如下操作。
③由极限思维可以知道我们可以通过初次判断n与1到k的和sum进行比较。(若小于或等于则存在这个序列,若大于则不可能存在这个序列(因为数在最初之和已经大于n了)。
④由①可以得,我们要找出1到n的关于n的所以因数,然后为了后期找最大公约数方便,要对因数进行排序。
⑤按照sum<=n/因数的条件。找到要的最大的那个因数。
⑥按照可能性先输出k-1的数,在输出n-(k-1)*因数的最后一个值。
⑦注意:时间的限制,在找因数,因数排序,找满足条件的最大因数中可以进行时间的缩短。
算法描述
#include<iostream>
#include<cmath>
using namespace std;
typedef unsigned long long ll;
void paixu(ll num[],long long j)
{
for(int i=0;i<j;i++){
ll x=i;
for(int k=i+1;k<=j;k++){
if(num[x]>num[k]) x=k;
}
if(x!=i){ //把大放在后面 ,小的放前面
ll temp=num[i];
num[i]=num[x];
num[x]=temp;
}
}
}
int main()
{
ll n,k;
cin>>n>>k;
if(k==1) cout<<n<<endl;
else{
ll sum=0,i,j=0,M,num[10000];
if(k&1) sum=(k+1)/2*k;
else sum=k/2*(k+1);
if(sum>n||k>=1e6) cout<<-1<<endl; //排除几个限制(此处不知为何不可以写成pow(10,10)
else{
for(i=1;i<=sqrt(n);i++){
if(n%i==0) {
num[j]=i;j++;
if(n/i!=i) {num[j]=n/i;j++;}
} //也可以不用考虑因子为n本身,因为当i=n时(①k=1,此时直接输出n;②k!=1,此时n<sum,则输出-1)这两种情况上面有
}
paixu(num,j-1);
/*for(int i=0;i<j;i++){
cout<<num[i]<<endl;
}*/
j--;
while(j>=0){
//cout<<num[j]<<endl;
if(sum<=n/num[j]) break; //找满足条件的最大因数
j--;
}
//cout<<M<<endl;
//cout<<n-sum*num[j]+k*num[j]<<endl;
i=0;
while(i++,i<k){
cout<<num[j]*i<<' ';
}
cout<<n-(sum-k)*num[j]<<endl;
}
}
return 0;
}
分析讨论与总结
①本题的题意理解(所有的最大公约数最大)可以知道是找他们之和的因数中满足条件的最大因数
②通过本题,知道了一些限制时间的代码写法。
③最后输出的时候可以总结的来写。