1. 程式人生 > 實用技巧 >Equal Numbers Gym - 101612E 思維

Equal Numbers Gym - 101612E 思維

題意:

給你n個數vi,你有k次操作。每一次操作你可以從n個數裡面挑一個數,然後使得這個數乘於一個正整數。操作完之後,這n個數裡面不同數的數量就是權值。你要使得這個值儘可能小。

題解:

如果a%b==0 && a!=b

  b%c==0 && b!=c

那麼如果我們進行操作的話,肯定是把c變成a,因為這樣消耗的最小運算元最少

我們最後的答案只需要在兩種情況中取最小值就可以了

1、我們把n個數都往n個數的公倍數上變

2、如果這n個數裡面出現了上面所示的a、b、c的情況,那麼我們就把b、c這些數都往a上變

過程:

因為題目求的是操作完之後不同數的數量,那麼我們首先記錄一下每一個數的出現次數

然後在提前處理一下每一個數的倍數是否在這n個數裡面

然後按照每一個數出現的次數排序。為什麼?給你一個序列

1 1 2 4

我們如果只有一個操作,那麼肯定是把2變成4.這樣的話答案就是2

這就意味著,我們要使得1變成4的話,那麼只有運算元大於等於3的時候才對最後的結果造成影響

之後就模擬就可以了

AC程式碼:

/*
有兩種操作,要麼是把一種數全部變成他的倍數(儘量大且存在的倍數),要麼是將盡量多種類的數變成所有數的公倍數。

那麼我們只需要維護出每種數是否存在其倍數,就只能這種數能否進行第一種操作。

只要對每個數因數分解,然後給因數打上標記,就可以知道每個數是否存在其倍數了。複雜度n*sqrt(t),t代表資料範圍。
*/ #include <algorithm> #include <cmath> #include <cstdio> #include <cstdlib> #include <cstring> #include <ctime> #include <iostream> #include <map> #include <queue> #include <set> #include <vector> using namespace std; typedef long long
ll; const int maxn = 1e6 + 5; const int INF=0x3f3f3f3f; const int mod = 1000000007; int v[maxn],num[maxn],vis[maxn],que1[maxn],que2[maxn]; int main() { freopen("equal.in","r",stdin); freopen("equal.out","w",stdout); int n; scanf("%d",&n); for(int i=1;i<=n;++i) { scanf("%d",&v[i]); num[v[i]]++; } for(int i=1;i<=n;++i) { int tmp=v[i]; if(tmp!=1) vis[1]=1; int ends=sqrt(tmp); for(int j=2;j<=ends;++j) { if(tmp%j==0) { vis[j]=1; vis[tmp/j]=1; } } } int pos1=0,pos2=0; for(int i=1;i<=1000000;++i) { if(num[i]==0) continue; int tmp=i; if(vis[tmp]) { que1[pos1++]=num[tmp]; } que2[pos2++]=num[tmp]; } sort(que1,que1+pos1); sort(que2,que2+pos2); //printf("%d****%d %d\n",pos1,que1[0],que1[1]); int ans1=0,ans2=0,tmp1=0,tmp2=0; printf("%d ",pos2); for(int i=1;i<=n;++i) { tmp1+=1; while(tmp1>=que1[ans1] && ans1<pos1) { tmp1-=que1[ans1]; ans1++; } tmp2+=1; while(tmp2>=que2[ans2] && ans2<pos2) { tmp2-=que2[ans2]; ans2++; } if(i!=n) printf("%d ",pos2-max(ans1,ans2-1)); else printf("%d\n",pos2-max(ans1,ans2-1)); } return 0; }