Solution
之前有遇到类似的题,第一步先考虑转化操作和问题。
对于每个数质因数分解成 \(\prod{p_i^{\alpha_i}}\),我们所需要的只有 \(\alpha_i\),因为只要求因子个数相同。
记其为 \(S_i = \{\alpha_1,\alpha_2,\dots,\alpha_k\}\),其中 \(\alpha_1 \geq \alpha_2 \geq \dots \alpha_k\)。
\(S_i\) 的个数不会超过 \(300\),因为 \(x,y \leq 10^6\),记其为 \(M\)。
将操作进行转化,对于操作一,它等效于对于集合 \(S_i\) 中一个 \(\alpha_i + 1\),对于操作二,它等效于新建了一个质因数。
考虑转移至目标状态,记 \(dp_{S_i,j}\) 表示由集合 \(S_i\) 转移至含有 \(j\) 个因数状态的最小操作数,对于初始的 \(dp_{S_1,0} = 0\),我们首先考虑 \(S_1\) 的转移,以下记 \(minp_i\) 表示 \(i\) 中最小的质因数:
\[dp_{S_1,i} = dp_{S_1,\frac{i}{minp_i}} + minp_i - 1 \]意为在目标质因数个数为 \(\frac{i}{minp_i}\) 时,需要 \(minp_i - 1\) 个因数来转移到 \(j\)。
为什么这样转移一定最优呢?
操作二每次新建质因数,即因数数量改变,故向最终答案逼近的越快。
现在考虑 \(dp_{S_i,j}\) 的转移,依据刚才的最优转移,得出:
我们所期望的最优方案,是存在质因数 \(p\),在 \(i\) 中出现过,\(j\) 中不含,且在 \(i\) 中出现次数最小,因为从 \(S_j \to S_i\) 的转移的花费为其出现次数。
故转移即可。
但是若 \(p\) 不存在呢?
对于方才的转移我们可以看做操作一,即修改 \(\alpha_i\) 最小的质因数,现在就是操作二,我们枚举新增质因数的倍数进行转移即可,即从 \(S_{k \times j} \to S_{j}\),其中 \(k \in [2,M]\)。
用 vector 存储状态和 dp 值,用 map 映射,预处理,随后每次询问取最小值即可。(注意不能转移直接调用 dp,因为每次是 \(\log n\) 的,很劣,复制一遍存下来,这样就是 \(O(1)\) 的查询)。
总复杂度 \(O(nM\sqrt{M} + tM)\)。
Code
#include <bits/stdc++.h>
#define int long long
using namespace std;
inline int read() {
int res = 0, f = 1;
char ch = getchar();
while (!isdigit (ch)) f = ch == '-' ? -1 : 1, ch = getchar();
while (isdigit (ch)) res = (res << 1) + (res << 3) + (ch ^ 48), ch = getchar();
return res * f;
}
void write (int x) {
if (x > 9) write (x / 10);
if (x < 0) x = -x, putchar ('-');
putchar (x % 10 + '0');
}
const int MAXN = 1e6 + 10, MAXM = 300;
int prime[MAXN], idx, minprime[MAXN], T, x, y;
bool isprime[MAXN];
vector <int> g[MAXN], Ans;
map < vector<int>, vector<int> > dp;
inline void Getprime() {
for (int i = 2; i < MAXN; i ++) {
if (!isprime[i])
prime[++ idx] = i, minprime[i] = i;
for (int j = 1; j <= idx && prime[j] * i < MAXN; j ++) {
isprime[prime[j] * i] = 1;
minprime[prime[j] * i] = prime[j];
if (!(i % prime[j])) break;
}
}
return;
}
inline void preDp() {
for (int i = 2; i < MAXN; i ++) {
g[i] = g[i / minprime[i]];
if (minprime[i] == minprime[i / minprime[i]]) g[i].back() ++;
else g[i].push_back (2);
}
for (int i = 2; i < MAXN; i ++)
sort (g[i].begin(), g[i].end(), greater_equal<int>());
dp[g[1]].resize (MAXM), dp[g[1]][1] = 0;
for (int i = 2; i < MAXM; i ++)
dp[g[1]][i] = dp[g[1]][i / minprime[i]] + minprime[i] - 1;
for (int i = 2; i < MAXN; i ++) {
if (!dp.count (g[i])) {
vector <int> tmp = g[i];
tmp.pop_back();
vector <int> &u = dp[tmp], &v = dp[g[i]];
v.resize (MAXM);
for (int j = 1; j < MAXM; j ++)
v[j] = u[j] + g[i].back() - 1;
for (int j = 1; j < MAXM; j ++) {
for (int k = 2; j * k < MAXM; k ++)
v[j * k] = min ({v[j * k], u[j] + labs(g[i].back() - k), v[j] + k - 1});
}
}
}
return;
}
signed main() {
Getprime(), preDp();
T = read();
while (T --) {
x = read(), y = read();
int tmpAns = 0x7f7f7f7f7f7f;
for (int i = 1; i < MAXM; i ++)
tmpAns = min (tmpAns, dp[g[x]][i] + dp[g[y]][i]);
Ans.emplace_back (tmpAns);
}
for (int Answer : Ans)
write (Answer), putchar ('\n');
return 0;
}
标签:Operations,CF1031F,int,题解,++,MAXM,alpha,质因数,dp
From: https://www.cnblogs.com/Ayaka-0928/p/18663273