[CF453B] Little Pony and Harmony Chest - 状压dp,数论
Description
对于一个正整数序列 (b_i),当且仅当它的任意两个元素都互质时,这个序列 (b_i) 才是和谐的。宝箱的钥匙是能让以下表达式的值最小的和谐序列 (b_i):(sum_{i=1}^n|a_i-b_i|)。得到了序列 (a_i),求 (b_i)。(n le 100, a_i le 30)
Solution
所有数的大小一定不超过 58,并且质因子的大小不超过 53
这样质数只有 16 个,考虑状压
设 (f[i][S]) 表示处理到第 i 位,当前用过的质因子集合 S,最小答案是多少
转移时枚举下一位填什么数,然后检查它的所有质因子是否 OK,每个数的质因子可以预处理并压成二进制,检查时候直接和 S 按位与就可以了,这样整个 dp 过程的复杂度为 (O(n m 2^{16}))
#include <bits/stdc++.h>
using namespace std;
#define int long long
signed main()
{
ios::sync_with_stdio(false);
int n;
cin >> n;
vector<int> a(n + 2);
for (int i = 1; i <= n; i++)
cin >> a[i];
vector<vector<int>> f(n + 2, vector<int>(65536, 1e18));
vector<vector<int>> from(n + 2, vector<int>(65536, 0));
f[0][0] = 0;
vector<int> primes;
for (int i = 2; i <= 53; i++)
{
int flag = 1;
for (int j = 2; j * j <= i; j++)
if (i % j == 0)
flag = 0;
if (flag)
primes.push_back(i);
}
vector<int> masks(66);
for (int i = 1; i <= 58; i++)
{
int mask = 0;
for (int j = 0; j < primes.size(); j++)
if (i % primes[j] == 0)
mask |= 1 << j;
masks[i] = mask;
}
for (int i = 1; i <= n; i++)
{
vector<int> g(65536, 1e18);
for (int j = 1; j <= 58; j++)
{
for (int s = 0; s < 65536; s++)
{
if (s & masks[j])
continue;
int t = s | masks[j];
f[i][t] = min(f[i][t], f[i - 1][s] + abs(a[i] - j));
if (f[i][t] == f[i - 1][s] + abs(a[i] - j))
{
from[i][t] = j;
}
}
}
}
vector<int> ans(n + 2);
int p = min_element(f[n].begin(), f[n].end()) - f[n].begin();
for (int i = n; i >= 1; i--)
{
ans[i] = from[i][p];
p &= ~masks[ans[i]];
}
for (int i = 1; i <= n; i++)
{
cout << ans[i] << " ";
}
cout << endl;
}