【數論思維題】Enlarge GCD【Codeforces Round #511 (Div. 2)】
阿新 • • 發佈:2018-12-11
題意:
給你 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; }