题目描述
k倍多重正整数集合的定义是:在一个多重集合(元素可以重复)中,不存在一个正整数是另一个正整数的k倍。
现在小M有n个正整数,你可以选择其中一些数构成k倍多重正整数集合。请求出最多能选出多少数来构成它。
输入描述:
第一行有两个整数n, k(1 <= n <= 10^5, 1 <= k <= 10^9)。 接下来一行有n个正整数a1, a2, ..., an (1 <= ai <= 10^9)。
输出描述:
最多能选出多少数构成k倍多重正整数集合。
示例1
输入
复制
6 2 2 3 6 5 4 10
输出
复制
3
说明
第一个样例中,我们选择2,3,5即可满足要求,因为k == 2,不存在一个数是另一个数的两倍。
示例2
输入
复制
2 2 2 2
输出
复制
2
说明
第二个样例中,我们选择2,2即可满足要求,因为k == 2,2也不是2的两倍,注意多重集合元素可以重复。
题解:假设dp[i][1] 表示选第i个数能够获得的最大值,dp[i][0]表示不选第i个数能够获得的最大值。
#include <bits/stdc++.h>
using namespace std;
const int N = (int)1e5 + 10;
int a[N];
int cot[N];
int flag[N];
int mark[N];
int dp[N][2];
map <int, int> mp;
vector <int> vec[N];
//dp[i][1]表示选第i个数获得最大值,dp[i][0]表示不选第i个数获得的最大值。
void dfs(int u) {
mark[u] = 1;
dp[u][1] = cot[u];
dp[u][0] = 0;
for (int v: vec[u]){
if(!mark[v]) {
dfs(v);
dp[u][1] = max(dp[u][1] + dp[v][0], dp[u][1]);
dp[u][0] += max(dp[v][1], dp[v][0]);
}
}
}
int main() {
int n, k;
cin >> n >> k;
for (int i = 0; i < n; i++) {
scanf("%d", a + i);
}
sort(a, a + n);
int h = 0;
cot[h] = 1;//表示第i个数的个数。
for (int i = 1; i < n; i++) {//去重
if(a[i] == a[h]){
cot[h]++;
} else {
a[++h] = a[i];
cot[h]++;
}
}
if (k == 1) {
cout << h << "\n";
return 0;
}
for (int i = 0; i <= h; i++) {
mp[a[i]] = i;
if (a[i] % k == 0 && mp.count(a[i] / k) > 0) {
//将有倍数关系的两个数加到树上。
vec[mp[a[i] / k]].push_back(i);
flag[mp[a[i] / k]] = 1;
flag[i] = 1;
}
}
int cnt = 0;
for (int i = 0; i <= h; i++) {
if (!flag[i]) {
cnt += cot[i];
} else if (!mark[i]) {
dfs(i);
cnt += max(dp[i][0], dp[i][1]);
}
}
cout << cnt << "\n";
return 0;
}