新光一2026-04-04 T2 P1414
P1414 又是毕业季II
题目背景
“叮铃铃铃”,随着高考最后一科结考铃声的敲响,三年青春时光顿时凝固于此刻。毕业的欣喜怎敌那离别的不舍,憧憬着未来仍毋忘逝去的歌。一千多个日夜的欢笑和泪水,全凝聚在毕业晚会上,相信,这一定是一生最难忘的时刻!
题目描述
彩排了一次,老师不太满意。当然啦,取每位同学的号数来找最大公约数显然不太合理。于是老师给每位同学评了一个能力值。于是现在问题变为,从 \(n\) 个学生中挑出 \(k\) 个人使得他们的默契程度(即能力值的最大公约数)最大。但因为节目太多了,而且每个节目需要的人数又不知道。老师想要知道所有情况下能达到的最大默契程度是多少。这下子更麻烦了,还是交给你吧~
PS:一个数的最大公约数即本身。
输入格式
第一行一个正整数 \(n\)。
第二行为 \(n\) 个空格隔开的正整数,表示每个学生的能力值。
输出格式
总共 \(n\) 行,第 \(i\) 行为 \(k=i\) 情况下的最大默契程度。
输入输出样例 #1
输入 #1
4
1 2 3 4
输出 #1
4
2
1
1
说明/提示
【题目来源】
lzn 原创
【数据范围】
记输入数据中能力值的最大值为 \(\textit{inf}\)。
- 对于 \(20\%\) 的数据,\(n \leq 5\),\(\textit{inf}\leq 10^3\);
- 对于另 \(30\%\) 的数据,\(n \leq 100\),\(\textit{inf} \leq 10\);
- 对于 \(100\%\) 的数据,\(n \leq 10^4\),\(\textit{inf} \leq 10^6\)。
思路
一、先看懂题目核心需求
题目简化:
给定 n 个数,对 k=1,2,...,n 分别输出:
从 n 个数里选 k 个数,它们的最大公约数的最大值。
示例:4 个数 [1,2,3,4]
k=1:选最大数 4 → gcd=4
k=2:选 2、4 → gcd=2
k=3:任意 3 个数,最大 gcd=1
k=4:全部选,gcd=1
二、整体解题思路(核心思想)
这道题不能暴力枚举(n=1e4,暴力会超时),正确做法是:
逆向思维:枚举公约数,统计能被它整除的数有多少个
统计每个数字出现的次数
对每个数 d,统计有多少个数是 d 的倍数 → 这个数量就是:最多能选 cnt[d] 个人,让他们的 gcd 至少是 d
最后处理答案:对每个 k,找到最大的 d 满足 cnt[d] ≥ k
时间复杂度O(\(inf\)\(log\)\(inf\))
inf=1e6,完全能通过题目限制
空间复杂度:
O(1e6)
,数组大小合规
思路简洁:筛法统计 + 倒推答案,是这道题的标准最优解法
本题核心是逆向枚举公约数 + 筛法统计,避免暴力超时
cnt[d] 表示有多少个数可以被 d 整除,即最多选 cnt[d] 人
倒序更新答案是为了让小 k 继承大 k 的最优解
#include<bits/stdc++.h>
using namespace std;
const int N=1e6+5;
int n,mx,cnt[N],ans[N];
int main(){
cin>>n;
for(int i=0,x;i<n;i++){cin>>x;cnt[x]++;mx=max(mx,x);}
for(int i=1;i<=mx;i++)
for(int j=i*2;j<=mx;j+=i)
cnt[i]+=cnt[j];
for(int i=1;i<=mx;i++)ans[cnt[i]]=max(ans[cnt[i]],i);
for(int i=n-1;i>=1;i--)ans[i]=max(ans[i],ans[i+1]);
for(int i=1;i<=n;i++)cout<<ans[i]<<'\n';
return 0;
}
浙公网安备 33010602011771号