传送:http://codeforces.com/gym/101612
题意:给出一个大小为n的序列a[i],每次选其中一个数乘以一个正整数,问进行k步操作后最少剩下多少种数字,输出0≤k≤n,所有的k的答案。
注意这k步不一定是连续的。
分析:
对于每个数,可以有两种操作:
1. 先将有倍数的数变成它们的最大倍数,而且按照出现次数比较少的先变。
2. 将所有数都变成lcm,而且按照出现次数比较少的先变。
数组f[i]代表,操作i次的最小种类数。对于每一次操作,取min。
1 #include<bits/stdc++.h> 2 using namespace std; 3 const int maxn=3e5+10; 4 const int inf=1e6+10; 5 map<int,int> mp; 6 vector<int> v[2]; 7 int f[maxn]; 8 int main(){ 9 freopen("equal.in","r",stdin); 10 freopen("equal.out","w",stdout); 11 int n,xx; cin >> n; 12 mp.clear(); 13 for (int i=0;i<n;i++){ 14 cin >> xx; 15 mp[xx]++; 16 } 17 int num=mp.size(); 18 v[0].clear(); v[1].clear(); 19 for (auto it:mp){ 20 int i=it.first; 21 v[0].push_back(it.second); 22 for (int j=i+i;j<inf;j+=i) 23 if (mp.count(j)){ 24 v[1].push_back(it.second); break; 25 } 26 } 27 memset(f,0x7f,sizeof(f)); 28 f[0]=num; 29 //变成该数的倍数(在数列中出现) 30 sort(v[1].begin(),v[1].end()); 31 int len=v[1].size(),sum=0; 32 for (int i=0;i<len;i++){ 33 sum+=v[1][i]; 34 f[sum]=min(f[sum],num-(i+1)); 35 } 36 //变成最小公倍数 37 sort(v[0].begin(),v[0].end()); 38 len=v[0].size(); sum=0; 39 for (int i=0;i<len;i++){ 40 sum+=v[0][i]; 41 f[sum]=min(f[sum],num-i); 42 } 43 cout << f[0]; 44 for (int i=1;i<=n;i++){ 45 if (i) f[i]=min(f[i],f[i-1]); 46 cout << " " << f[i]; 47 } 48 cout << endl; 49 return 0; 50 }