1. 程式人生 > >【數論思維題】Enlarge GCD【Codeforces Round #511 (Div. 2)】

【數論思維題】Enlarge GCD【Codeforces Round #511 (Div. 2)】

題意:

      給你 n 個數,分別為 a1、a2、...、ai、... an,現在從這n個數中刪去p個數,使剩下的數的gcd變大。求最小的p。

思路:

      一開始的思路是想用貪心做,先求出所有數字的gcd,然後將數字排序,再依次求gcd,如果與當前這個數字求出的gcd等於開始的那個gcd,就將這個數字刪掉。

      然後wa 8,很明顯這個方法是錯的,比如 2 4 7 14 21 這個序列,用這種方法就會出錯。

      接下來是正解。N = 1.5*1e7。

      先對所有數求出gcd,然後對1-N內所有數進行標記,若題目中給出了 a[i],則 vis[a[i]]++,然後採用一種類似埃氏篩的方法。

       i 從gcd+1到N進行迴圈,對於p[i] == 0,就將 i 的所有倍數k,p[k]標記為1,然後記錄 i 的所有倍數中有cnt個在最開始題目給出的數列中出現了,那麼對於這cnt個數,他們的gcd一定大於初始數列的gcd,因此問題就轉化為了求cnt的最大值。

      本問題就解決了。

反思:

      這道題拿到手上之後,思路先是跑到了最簡單的貪心,然後wa了之後,開始將每個數唯一分解進行篩數,與正解思想類似,但是正解是用所有數去篩,而唯一分解是用素數去篩,過了評測,但是終測掛了。

      結束之後看到正解之後還是有一些小遺憾的,畢竟正解程式碼非常短......(比唯一分解什麼的短多了......)

      以後寫題還是要考慮清楚,不要進行冗餘操作,也不要漏操作。

程式碼:

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#define rep(i,a,b) for(int i = a;i <= b;i++)
using namespace std;
const int N = 1.5*1e7+1000;

int a[N],b[N];
int n;

int gcd(int a,int b)
{
	return b == 0 ? a:gcd(b,a%b);
}

int main()
{
	int d;
	scanf("%d",&n);
	scanf("%d",&d);
	a[d]++;
	rep(i,2,n){
		int x;
		scanf("%d",&x);
		a[x]++;
		d = gcd(d,x);
	}
	int ans = 0;
	rep(i,d+1,N-500)
	{
		if(b[i] == 0)
		{
			int cnt = 0;
			for(int j = i; j <= N-500; j += i)
			{
				b[j] = 1;
				cnt += a[j];
			}
			ans = max(ans,cnt);
		}
	} 
	if(ans == 0)
		printf("-1\n");
	else printf("%d\n",n-ans);
	return 0;
}