[Luogu] CF364D Ghd

Description

选出\(\ge\lceil\frac{n}{2}\rceil\)个数字使得他们的最大公约数最大。

Solution

神奇的随机化。

注意到一定会选\(\ge\lceil\frac{n}{2}\rceil\)个数,那么在原序列中随机选一个数,在答案序列里的概率为\(\frac{1}{2}\)。那么我们连续选\(10\)次,全错的概率就只有\(\frac{1}{2^{10}}<\frac{1}{1000}\),已经很小了。(但我要跑\(11\)次)

对于一个选出来的数\(x\),我们先把原序列的所有数和\(x\)取一个\(\gcd\),再依次枚举可能为答案的\(\gcd\),如果它和它的倍数在序列中出现次数\(\ge\lceil\frac{n}{2}\rceil\),那么就可以更新答案。注意到\(\gcd\)可能有很多相同的,要先去重,否则会炸。

Code

#include <bits/stdc++.h>

using namespace std;

#define ll long long

ll t, n, res, a[1000005], b[1000005], c[1000005], s[1000005];

ll read()
{
	ll x = 0ll, fl = 1ll; char ch = getchar();
	while (ch < '0' || ch > '9') { if (ch == '-') fl = -1ll; ch = getchar();}
	while (ch >= '0' && ch <= '9') {x = (x << 1ll) + (x << 3ll) + ch - '0'; ch = getchar();}
	return x * fl;
}

ll gcd(ll x, ll y)
{
	return y ? gcd(y, x % y) : x;
}

int main()
{
	n = read();
	for (ll i = 1; i <= n; i ++ )
		a[i] = read();
	t = 11ll;
	while (t -- )
	{
		ll pos = 1ll * rand() * rand() % n + 1ll;
		ll x = a[pos];
		for (ll i = 1; i <= n; i ++ )
			b[i] = gcd(a[i], x);
		sort(b + 1, b + n + 1);
		ll cnt = 0ll, tt = 0ll;
		for (ll i = 1; i <= n; i ++ )
		{
			if (b[i] != b[i + 1])
			{
				tt ++ ;
				c[tt] = b[i];
				s[tt] = cnt + 1ll;
				cnt = 0ll;
			}
			else cnt ++ ;
		}
		ll ans = 0ll;
		for (ll i = 1; i <= tt; i ++ )
		{
			ll sum = 0ll;
			for (ll j = 1; j <= tt; j ++ )
				if (c[j] % c[i] == 0ll)
					sum += s[j];
			if (sum >= (n + 1ll) / 2ll) ans = max(ans, c[i]);
		}
		res = max(res, ans);
	}
	printf("%lld\n", res);
	return 0;
}
posted @ 2020-11-14 08:08  andysj  阅读(52)  评论(0编辑  收藏  举报