CodeForces - 803C

C. Maximal GCD
time limit per test
1 second
memory limit per test
256 megabytes
input
standard input
output
standard output

You are given positive integer number n. You should create such strictly increasing sequence of k positive numbers a1, a2, ..., ak, that their sum is equal to n and greatest common divisor is maximal.

Greatest common divisor of sequence is maximum of such numbers that every element of sequence is divisible by them.

If there is no possible sequence then output -1.

Input

The first line consists of two numbers n and k (1 ≤ n, k ≤ 1010).

Output

If the answer exists then output k numbers — resulting sequence. Otherwise output -1. If there are multiple answers, print any of them.

Examples
input
Copy
6 3
output
Copy
1 2 3
input
Copy
8 2
output
Copy
2 6
input
Copy
5 3
output
Copy
-1

原题是这个,在昨天和河大的联赛里给改成了中文,难度降低了很多(ಥ _ ಥ)中文学的还不怎么样呢 再去理解英语那些拐弯抹角滴是真滴心塞塞啊 

题意:

请构造一个长度为k的严格递增的数组,要求这k个数的最大公约数尽可能的大,且这k个数的和为n

如果不存在这样的数组请直接输出 -1.

Input

输入两个正整数 n 和 k (1 ≤ n, k ≤ 1010).

Output

如果答案存在则输出答案: k 个数,用空格间隔.如果有多个答案满足条件,输出其中一种情况就行 否则就输出 -1.

刚一看题就很懵逼 其实细细分析 还没很难ヾ(。 ̄□ ̄)ツ゜゜゜额...也不知道谁给我的勇气说不难哈哈哈

其实大概就可以用下面的公式来表达:q表示公约数,a1,a2...ak表示k个数


上面的式子也可以化简为:


题目上要求最大的公约数所以a1+a2+a3+~+ak的最小值是(1+2+~+(k-1)+k)的情况;

即:

当他们之和小于n/q时,我们可以把k的值一直往后加,直到他们的和等于n/q,于是就有了下面的代码:

#include<cstdio>
long long n,k;
int main()
{
	while(scanf("%lld %lld",&n,&k)!=EOF)
	{
		long long sum,sum1,s=1;
		int flag=0;
		sum1=sum=k*(k+1)/2;
		if(sum>n)
		{
			flag=1;
		}else
		{
			while(n%sum)
			{
				sum++;
			}
			s=n/sum;
		}
		if(flag)
		{
			printf("-1\n");
		}	
		else
		{
			for(long long i=1;i<k;i++)
			{
				printf("%lld ",s*i);
			}
			printf("%lld\n",s*(k+sum-sum1));
		}
	}
}

但是...因为题目中的数据比较大,如果循环遍历ak的值+1,当差值很大时,时间会超限:

while(n%sum)//sum表示a1~ak的总和
{
    sum++;
}
s=n/sum;//求出来满足条件的最大公约数

就是这一小点,不要小看它,真的很费时间...我百思不得其解,于是我看了题解..大概似懂非懂。

既然直接求ak的话会超时,我们已经确定了a1~ak-1的和,那么我们可以找出来最大的公约数(因为最大公约数的求法阔以优化ヾ(。 ̄□ ̄)ツ゜゜゜只需要O(sqrt(n))的时间复杂度),

套用就可以求出来m,

最大公约数的求法:

for(long long i=1;i<=sqrt(n);i++)
    {
	    if(n%i==0)//如果==0,i是约数,n/i也是约数,
	    {
	        if(i>=(1+k)*k/2)//判断i是否大于等于1~k的和,如果大于等于表示i满足a1~ak和的情况
		{
			q=n/i;//所以公约数是n/i;
			break;//第一个大于(1+~+k)值的它的公约数一定是最大的!!!
		}
                else if(n/i>=(1+k)*k/2)判断n/i是否大于等于1~k的和,同上的思想
                     q=i;//但是n/i不一定是最小的,所以不break;
            }
     }

如果想明白的话还是很好懂的,

为什么是循环只遍历到sqrt(n)?


假如n=21,如果1是n的约数,那么n/1=21也是它的约数,同理,3是n的约数,n/3=7也是它的约数,一直到sqrt(n的时候,两个约数是相等的,sqrt(n之后就相当于是从1~sqrt(n再遍历一遍,(φ(≧ω≦*)♪多想几遍你就能懂了~加油)

2.两个判断条件,前面我们说到约数问题,n%i==0表示i,n/i都是n的约数,前面我们说了(1+2+~+k)<=n,它是满足条件的最初的一个状态,如果他等于n话直接可以输出,如果不的话,我们就要找出一个a1+~+ak的值大于(1+2+~+k)的。((ಥ _ ಥ)可能说的还不是很清楚,但大概就是这样,再多想想,多想想,代码我给的注释也很清楚,两者结合一下哈ヾ(=゚・゚=)ノ喵♪)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值