首页 > 其他分享 >题解:CF1031F Familiar Operations

题解:CF1031F Familiar Operations

时间:2025-01-10 09:29:12浏览次数:1  
标签:Operations CF1031F int 题解 ++ MAXM alpha 质因数 dp

传送门

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

Submission

#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

相关文章

  • Atcoder ABC329E Stamp 题解 [ 绿 ] [ 线性 dp ]
    Stamp:难点主要在dp转移的细节与分讨上,但通过改变状态设计可以大大简化分讨细节的题。观察首先要有一个观察:只要某一个前缀能被覆盖出来,那么无论它后面多出来多少,后面的字符串都可以帮他重新覆盖回去。这也就是dp为啥没有后效性的原因。除此之外,还要注意一个字符串不仅能在......
  • 跟我一起学 Python 数据处理(二十六):PDF 数据提取技巧与问题解决策略
    跟我一起学Python数据处理(二十六):PDF数据提取技巧与问题解决策略在Python数据处理的学习之旅中,我们已经走过了不少路程,今天继续深入探索PDF文件处理的核心技巧与方法,旨在帮助大家进一步提升数据处理能力,解决实际工作中遇到的难题。一、slate库处理PDF文件的深入......
  • D. Smithing Skill 和 D. Grid Puzzle的题解
    D.SmithingSkill:https://codeforces.com/problemset/problem/1989/D思路:https://blog.csdn.net/weixin_73936404/article/details/140045020(看这位的博客吧,这个本人第一次写卡住了,题解就当复盘了)贪心:优先消耗值小的(花费和返回的差值)且门槛小的。代码:#include<bits/stdc......
  • [NOI2018] 你的名字的题解
    [NOI2018]你的名字Solution:考虑一下\(l=1,r=\left|S\right|\)的时候怎么做,其实比较简单,我们对\(S,T\)都建立出SAM,利用这个求得\(p_i\),表示\(T_{i-p_i+1,i}\)在\(S\)上是一个连续子串,设\(fir_i\)表示\(T\)的SAM中,节点\(i\)代表的\(endpos\)中的最小值(事实上......
  • 一些比赛的题解
    A把第二个字符串反转,然后对于第一个字符串中为#的位置,输出第二个字符串中对应位置的字符即可。B考虑枚举答案(需要注意不能二分),假设当前枚举的答案为\(res\),只需考虑怎么判定该答案是否合法。不难发现,找到\(res\)的不同的两个倍数同时属于这个区间,\(res\)就是合法的。C......
  • G. D-Function 题解 (快速幂, 组合数学)
    原题链接:https://codeforces.com/contest/1985/problem/G题目:思路:要满足D(kn)==kD(n),k与n的每一位相乘都不能发生进位,k只能是一位数。考虑n的位数可能有1e9,所以用到了快速幂。代码:#include<bits/stdc++.h>#defineintlonglongusingnamespacestd;constintmod......
  • 学校月考题解 #2
    一些闲话期末考,依旧是AK。题解T1有\(n\)个位置。起初每个位置都被封锁。你可以进行以下两种类型的操作:选择一个位置\(i\),其中\(1\leqi\leqn\),然后解除该位置的封锁;选择一对位置\(l\)和\(r\),其中\(1\leql\leqr\leqn\),满足位置\(l\)和\(r\)都已解除封锁,......
  • [BZOJ3159] 决战 题解
    个人感觉各方面难度高于《在美妙的数学王国中畅游》,也不知道是不是求导的关系,这题\(luogu\)难度评级还更低。不过感觉这题作完对\(LCT\)理解更顺畅了。前四个操作简单,关键在第五人格操作。注意力惊人的注意到我们无法像普通\(Splay\)一样,直接对\(LCT\)中的\(Splay\)......
  • Kubernetes集群运维生产常见问题解析与解决方案
    前言:在Kubernetes集群的日常运维工作中,我们难免会遇到各种各样的问题。这些问题可能涉及到集群的部署、配置、监控、性能优化等多个方面。为了解决这些问题,我们需要不断地学习和积累经验。在这里,我打算收集并整理一些网友曾经提出的问题,并提供相应的解析和解决方案,之前的问题无从......
  • 2024 年 06 月 GESP C++ 一级真题解析
    ......