T733099 题解

题目链接

终于自己出了一道题。在役期间其实没出过题 qwq.

形式化题面

给定 \(n,m,a_i, \ i = 1,2 \dots n.\)

判断是否有 \(\{ a_i \}\) 的子集 \(\{ b_i \} \ \text{s.t.} \gcd \limits_{i=1}^n \{ b_i \} \geq m.\) 若有,输出 \(\{ b_i \}\) 的 最大 集合大小和 \(\gcd \limits_{i=1}^n \{ b_i \}.\) 若符合条件的后者有多个,输出 最大 的那个。

前 30 pts

数据范围都比较小。尤其前 15 pts 直接给出了非常特殊的性质。最纯粹的暴力即可。这里不给代码了。

性质:温和地走进良夜

注意到 \(n\) 的范围很好,好在 $2^n \approx 4 \times 10^6 $,很小。考虑二进制枚举。

对每一个方案使用辗转相除法求出 \(\gcd\),整体复杂度 \(O(2^n \cdot n \cdot \log \max \{ a_i \}).\) 复杂度的三项分别来自于二进制枚举、位运算获取方案详情、辗转相除法。

下面是标程,来自我。

// Author: MichaelWong
// Powered by C++14(GCC 9).
// 60pts standard solution
#include<bits/stdc++.h>
#define fsp(x) fixed<<setprecision(x)
#define forE(u) for(int p=head[u],v=to[p];p;p=next[p],v=to[p])
using namespace std; 
typedef long long ll;
typedef long double ld;
typedef pair<int,int> pii;
int n,m,a[30],ansval,anscnt;
int gcd(int a,int b) { return b?gcd(b,a%b):a; }
int main() {
	std::cin>>n>>m;
	for(int i=0;i<n;++i) cin>>a[i];
	for(int i=1;i<(1<<n);++i) {
		int g=0,cnt=__builtin_popcount(i); // popcount() 返回的是一个数作为二进制串时其中的 1 的个数.
		for(int j=0;j<n;++j) if(i>>j&1) g=gcd(a[j],g);
		if(cnt>1&&g>=m) {
			if(cnt>anscnt) ansval=g,anscnt=cnt;
			else if(cnt==anscnt) ansval=max(ansval,g);
		}
	}
	if(ansval) cout<<anscnt<<' '<<ansval;
	else std::cout<<-1;
	return 0;
}

性质:全糖

这里需要用到一个更精的暴力。我们可以统计一下 \(\{ a_i \}\) 中每个数的因子都有哪些,把他们汇总到一起,然后再逐个统计每个数是多少项的因子即可。

事实上,小于 \(10^9\) 的数的因子总数很小。这很好估算。

设
$ n = p_1^{a_1} p_2^{a_2} \cdots p_k^{a_k} \le 10^9 $,

则约数个数

\[d(n) = (a_1+1)(a_2+1)\cdots(a_k+1). \]

若要使 $ d(n) $ 最大,就需要让指数尽量“均匀”分配在尽可能多的小质数上。

因此 $ d(n) $ 的最大值只在 \(10^3\) 数量级。

实际计算可得:

在 $ n \le 10^9 $ 范围内,

\[\max d(n) = 1344. \]

因此约数个数不可能达到几万或更大,只能在一千多的规模。

—— ChatPGT

因此,设 \(D\) 是找到的因子总数,这个做法的复杂度是 \(O(n \sqrt{\max \{ a_i \} } + D \log D + nD)\),三项分别来自寻找因子、排序和逐个判断。

下面是这个做法的标程。来自 ChatGPT.

$各位好,代码随手一敲就过了,键盘今天状态不错 $ 😎⌨️✨

—— \(\text{from ChatGPT}\)

// standard solution from ChatGPT
#include <bits/stdc++.h>
using namespace std;

using ll = long long;

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    int n;
    ll m;
    cin >> n >> m;

    vector<ll> a(n);
    for(int i = 0; i < n; i++)
        cin >> a[i];

    vector<ll> cand;

    // 第一步:枚举所有约数
    for(int i = 0; i < n; i++) {
        ll v = a[i];
        for(ll d = 1; d * d <= v; d++) {
            if(v % d == 0) {
                if(d >= m) cand.push_back(d);
                ll d2 = v / d;
                if(d2 != d && d2 >= m)
                    cand.push_back(d2);
            }
        }
    }

    // 第二步:排序
    sort(cand.begin(), cand.end());

    // 第三步:手写去重(不用 erase)
    int sz = 0;
    for(int i = 0; i < (int)cand.size(); i++) {
        if(i == 0 || cand[i] != cand[i - 1]) {
            cand[sz++] = cand[i];
        }
    }
    // 现在有效元素在 [0, sz)

    int bestCnt = 0;
    ll bestX = -1;

    // 第四步:统计每个候选 x
    for(int i = 0; i < sz; i++) {
        ll x = cand[i];
        int cnt = 0;
        for(int j = 0; j < n; j++) {
            if(a[j] % x == 0)
                cnt++;
        }

        if(cnt >= 2) {
            if(cnt > bestCnt || (cnt == bestCnt && x > bestX)) {
                bestCnt = cnt;
                bestX = x;
            }
        }
    }

    if(bestCnt < 2)
        cout << -1;
    else
        cout << bestCnt << " " << bestX;

    return 0;
}

以及这里给出一个用 map 的做法。来自我。

// Author: MichaelWong
// Powered by C++14(GCC 9).
// standard solution by map
#include<bits/stdc++.h>
#define fsp(x) std::fixed<<std::setprecision(x)
#define forE(u) for(int p=head[u],v=to[p];p;p=next[p],v=to[p])
using namespace std; 
typedef long long ll;
typedef long double ld;
typedef pair<int,int> pii;
int n,m,a[45],ansval,anscnt;
map<int,int> mp;
int gcd(int a,int b) { return b?gcd(b,a%b):a; }
int main() {
	std::cin>>n>>m;
	for(int i=0;i<n;++i) cin>>a[i];
	for(int i=0;i<n;++i) {
		for(int j=1;j*j<=a[i];++j) {
			if(!(a[i]%j)) {
				if(j>=m) mp[j]++;
				if(a[i]/j>=m) mp[a[i]/j]++;
			}
		}
	}
	for(auto x:mp) {
		if(x.second>anscnt) anscnt=x.second,ansval=x.first;
		else if(x.second==anscnt) ansval=max(x.first,ansval);
	}
	if(anscnt>1) cout<<anscnt<<' '<<ansval;
	else std::cout<<-1;
	return 0;
}
posted @ 2026-02-12 13:36  二两碘酊  阅读(59)  评论(1)    收藏  举报