Logo zrl123456 的博客

博客

CF803C Maximal GCD 讲解

...
zrl123456
2025-12-01 12:51:25
我咋啥也不会/ll

本文章由 WyOJ Shojo 从洛谷专栏拉取,原发布时间为 2024-01-12 18:54:23

题目
相信很多人是被数据范围劝退的 $\dots\dots$
数据范围为:
$$1\le n,k\le 10^{10}$$ 你可能觉得输出 $10^{10}$ 个数是不可能的事情。
但是我们思考一下:
都知道高斯的求和公式吧:
$$\sum_k^{i=1} i=n(n+1)/2$$ 因为 $(1+1.5\times 10^5)(1.5\times 10^5)/2>10^{10}$, 所以当 $k\ge 1.5\times 10^5$ 的时候, 直接输出 $-1$ 就可以了。
上述行为是为了防止爆 long long, 再次特判, 若 $k(k+1)/2>n$, 则依然输出 $-1$。
然后我们设序列的第 $i$ 个数为 $a_i$, 最大公约数为 $m$, 根据同余定理, 不难得到:
$$n\bmod m=(\sum_k^{i=1}a_i)\bmod m=(\sum_k^{i=1}(a_i\bmod m))\bmod m=0$$ 所以, 我们需要找出 $n$ 的全部因数, 从大到小排个序, 做完上述步骤后的时间复杂度为 $O(\sqrt n+k\log_2k)$,简化为 $O(k\log_2 k)$, 是可承受的。
下一步我们枚举 $a_i$, 直到找到符合 $a_i\times (k(k+1)/2)\le n$ 条件的数, 最后输出即可, 此处的复杂度仅仅只有 $O(\sqrt n+k)$。
代码:

#include<bits\/stdc++.h>
using namespace std;
long long n,k,a[100005],summ,maxn;
bool cmp(long long x,long long y){ return x>y; }
void work(){
	for(long long i=1;i*i<=n;i++)
		if(n%i==0)
			if(i*i==n) a[++summ]=i;
			else{
				a[++summ]=i;
				a[++summ]=n\/i;
			}
	return;
}
int main(){
	cin>>n>>k;
	if(k>=150000){
		cout<<-1;
		return 0;
	}
	if((1+k)*k\/2>n){
		cout<<-1;
		return 0;
	}
	work();
	sort(a+1,a+summ+1,cmp);
	for(int i=1;i<=summ;i++)
		if(n*2\/k\/(k+1)>=a[i]){
			maxn=a[i];
			break;
		}
	for(int i=1;i<k;i++){
		cout<<i*maxn<<' ';
		n-=i*maxn;
	}
	cout<<n;
	return 0;
} 

评论

暂无评论

发表评论

可以用@mike来提到mike这个用户,mike会被高亮显示。如果你真的想打“@”这个字符,请用“@@”。