正题
luogu
CF364D
题目大意
给你一个大小为n的集合,选择一个至少为一半的子集,另其gcd最大
解题思路
由于数字个数很多,考虑随机
随机选10个数,对于每个数,先处理出约数,然后求出所有数和当前数的gcd,这些gcd的约数权值全部加1(权值意义为gcd为这个点,集合最多选多少个点),最后找到最大满足权值大于一半数即可
求出10个数的答案之后求个max即可,因为集合大于一半,所以正确率为 1−12101-\frac{1}{2^{10}}1−2101
时间复杂度为 O(n+d2)O(n+d^2)O(n+d2),随机的数再多就会TLE了
code
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define ll long long
#define N 1001000
using namespace std;
int n,w,now,v[N];
ll x,ans,a[N],s[N];
void get(ll x)
{w=0;for(ll i=1;i*i<=x;++i)if(x%i==0){w++,s[w]=i,v[w]=0;if(x/i!=i)w++,s[w]=x/i,v[w]=0;}sort(s+1,s+1+w);return;
}
ll gcd(ll x,ll y)
{return (y?gcd(y,x%y):x);
}
int main()
{scanf("%d",&n);for(int i=1;i<=n;++i)scanf("%lld",&a[i]);for(int g=1;g<=10;++g){x=a[rand()*rand()%n+1];get(x);for(int i=1;i<=n;++i){now=lower_bound(s+1,s+1+w,gcd(x,a[i]))-s;v[now]++;}for(int i=1;i<=w;++i)for(int j=i+1;j<=w;++j)if(s[j]%s[i]==0)v[i]+=v[j];for(int i=w;i>0;--i)if(v[i]>=(n+1)/2)ans=max(ans,s[i]);}printf("%lld",ans);return 0;
}