问题 D: 最大公约数
时间限制: 1 Sec 内存限制: 128 MB
[提交] [状态]
题目描述
给定n个正整数,a_1,a_2,…,a_n,求最少删去几个数,使得删去后这些数的最大公约数比原先的所有数的最大公约数大。
输入
第一行一个整数n,第二行n个正整数,a_1,a_2,…,a_n。
输出
一个数,表示最少删去的个数,若无论怎么删都不会比原来的大,输出-1。
样例输入 Copy
3
1 2 4
样例输出 Copy
1
提示
删去1这个数,最大公约数从1变到2。
对于30%的数据,n<=15
对于50%的数据,n,a_i<=1000
对于100%的数据,n<=300,000,a_i<=1.5*10^7
网课老师讲的太无聊,于是借了个号水了道题、
容易转化为,
草、不会描述,看下这个代码吧
//先把所有数÷gcd,变成互质
//然后进行下面代码
for(int i=0;i<cnt;i++){//枚举素数
int cot = 0;//统计这个素数是多少个数的因子
for(int j=1;j<=n;j++)if(A[i]%prim[i] == 0){
++cot;
}
ans = min(ans,n-cot);//更新答案
}
把ai的最大值记作M吧
复杂度,sqrt(M)*sqrt(n) 即,(sqrt(1.5e7)*3e5,有点大,
考虑把每个数的素因子搞(存)一下
vector用链式前向星代替、这样复杂度大概
sqrt(M)*(M/2+M/3+M/5+M/7.....)
不会算、、暴力跑了下差不多是2*M,也就是3e7
void init(){
for(int i=2;i<N;i++){//N = sqrt(M)
if(!vis[i]){
for(int j=1LL*i*i;j<M;j+=i){
if(j<N)vis[j] = 1;
if(flag[j])Add(j,i);//只需要记录在A数组的即可
}
}
}
}
处理好每个数的素因子之后,枚举每个数的每个素因子即可
for(int i=1;i<=n;i++){
for(int j=head[A[i]];j;j=Next[j]){
int did = To[j];
while(A[i] % did == 0)A[i] /= did;
++flag[did];
ans = min(ans,n-flag[did]);
}
if(A[i] != 1)++flag[A[i]],ans = min(ans,n-flag[A[i]]);
}
ps:
有一点就是内存卡得厉害,4e7开不出来,于是拿ordered_map写的链式前向星的head数组
next和to数组大小应该开8*maxn,因为2*3*5*7*11*13*17*19=9e6,每个数最多8不重复的个质因子
但是数据水,无所谓了、
完整代码
upc跑了大概500ms
/*author: revolIA*/
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define min(a,b) (((a)<(b))?(a):(b))
const int maxn = 3e5+7,M = 1.5e7+7;
const int N = 1e4+7;
bool vis[N];
unordered_map<int,int> head;
int Next[maxn<<1];
int To[maxn<<1];
int tot;
void Add(int u,int v){
To[++tot] = v;
Next[tot] = head[u];
head[u] = tot;
}
int flag[M];
void init(){
for(int i=2;i<N;i++){
if(!vis[i]){
for(int j=1LL*i*i;j<M;j+=i){
if(j<N)vis[j] = 1;
if(flag[j])Add(j,i);
}
}
}
}
int n,A[maxn],gcd;
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d",&A[i]);
if(gcd != 1)gcd = i==1?A[i]:__gcd(A[i],gcd);
}
if(gcd != 1)for(int i=1;i<=n;i++)A[i] /= gcd;
for(int i=1;i<=n;i++)flag[A[i]] = 1;
init();
for(int i=1;i<=n;i++)flag[A[i]] = 0;
int ans = n;
for(int i=1;i<=n;i++){
for(int j=head[A[i]];j;j=Next[j]){
int did = To[j];
while(A[i] % did == 0)A[i] /= did;
++flag[did];
ans = min(ans,n-flag[did]);
}
if(A[i] != 1)++flag[A[i]],ans = min(ans,n-flag[A[i]]);
}
printf("%d\n",ans<n?ans:-1);
return 0;
}