Codeforces Round #694 (Div. 2) D. Strange Definition【质因数分解 | 数论】

题意:

定义如果两个数 x , y x, y x,y “相邻”,那么 l c m ( x , y ) g c d ( x , y ) \frac{lcm(x, y)}{gcd(x, y)} gcd(x,y)lcm(x,y) 是平方数,给定长度为 n n n 的数字数组 a a a,其中的每个元素 a i a_i ai 每一秒后都会被所有与 a i a_i ai 相邻(包括 a i a_i ai 自己)的数字的乘积替换,定义 d i d_i di 为与 a i a_i ai 相邻的数字的个数,则数组 a a a b e a u t y beauty beauty m a x ( d i ) max(d_i) max(di),给出 q q q 次询问,每次询问是一个数字 w w w,回答 w w w 秒后数组 a a a b e a u t y beauty beauty 是多少。其中 1 ≤ n ≤ 3 ⋅ 1 0 5 1 \le n \le 3 \cdot 10^5 1n3105, 1 ≤ a i ≤ 1 0 6 1 \le a_i \le 10^6 1ai106, 1 ≤ q ≤ 3 ⋅ 1 0 5 1 \le q \le 3 \cdot 10^5 1q3105, 0 ≤ w ≤ 1 0 18 0 \le w \le 10^{18} 0w1018


思路:

  1. 经过简单的变换,我们可以将 l c m ( x , y ) g c d ( x , y ) \frac{lcm(x, y)}{gcd(x, y)} gcd(x,y)lcm(x,y) 化为 x ⋅ y g c d ( x , y ) 2 \frac{x \cdot y}{gcd(x, y)^2} gcd(x,y)2xy,即若 x x x y y y 相邻是当且仅当 x ⋅ y x \cdot y xy 为平方数。
  2. 定义 a l p h a i alpha_i alphai 为数字 p a l p h a i p^{alpha_i} palphai 能整除 x 的最大的 a l p h a i alpha_i alphai, 同理于 b e t a j beta_j betaj 对于 y,则若 p α i ⋅ p β j p^{\alpha_i} \cdot p^{\beta_j} pαipβj 为平方数时当且仅当 α i + β j \alpha_i + \beta_j αi+βj 是偶数,即 α i ≡ β j \alpha_i \equiv \beta_j αiβj(mod 2)。
  3. x x x y y y 分别进行质因数分解后我们可得 x = p 1 α 1 ⋅ … ⋅ p k α k x =p_1^{\alpha_1} \cdot \ldots \cdot p_k^{\alpha_k} x=p1α1pkαk y = p 1 β 1 ⋅ … ⋅ p k β k y =p_1^{\beta_1} \cdot \ldots \cdot p_k^{\beta_k} y=p1β1pkβk,根据 2,我们将 x x x y y y 分别替换为 x = p 1 α 1   m o d   2 ⋅ … ⋅ p k α k   m o d   2 x = p_1^{\alpha_1\bmod2} \cdot \ldots \cdot p_k^{\alpha_k\bmod2} x=p1α1mod2pkαkmod2 y = p 1 β 1   m o d   2 ⋅ … ⋅ p k β k   m o d   2 y = p_1^{\beta_1\bmod2} \cdot \ldots \cdot p_k^{\beta_k\bmod2} y=p1β1mod2pkβkmod2,那么经过这样的替换后若两个数相邻当且仅当 x 和 y 相等,原因如下:2 我们可知,若 x x x y y y 相邻,则 p 1 α 1 , … , p k α k p_1^{\alpha_1}, \ldots, p_k^{\alpha_k} p1α1,,pkαk p 1 β 1 , … , p k β k p_1^{\beta_1}, \ldots, p_k^{\beta_k} p1β1,,pkβk 中每个都要满足 α i ≡ β i \alpha_i \equiv \beta_i αiβi,因为 p 1 , … , p k p_1, \ldots, p_k p1,,pk 都为质数,因此若不满足上述条件则 x x x y y y 必然某个质因子的指数不同,也就造成了 x x x y y y 不同,反之若满足上述条件,则 x x x y y y 必然 “相邻”
  4. 将数组中的每个元素进行替换,然后将相同元素分为一组。(1) 若组中有偶数个元素,那么下一秒这些元素都将变为 1 1 1原因是: 因为有偶数个元素,那么对于最终乘积 z = p 1 α 1 , … , p k α k z = p_1^{\alpha_1}, \ldots, p_k^{\alpha_k} z=p1α1,,pkαk 中每个 α i \alpha_i αi(mod 2),都满足 α i = 0 \alpha_i = 0 αi=0,也就是 z = 1 z = 1 z=1。(2) 若组中有奇数个元素,则下一秒组中的这些元素都不变,原因类似于偶数情况。因为 1 和奇数组都不再改变,而偶数组都会变为 1,因此答案也就只有 0 0 0 秒时和 1 1 1 秒后这两种情况,因此 a n s 0 ans_0 ans0 0 0 0 秒时最大的组的大小,而因为 1 1 1 秒后数组中 1 的个数会增加,因此 a n s 1 ans_1 ans1 为一秒后 1 1 1 的个数和 a n s 0 ans_0 ans0 的最大值。

做法:

  • 做法 1 : 由于 1 ≤ a i ≤ 1 0 6 1 \le a_i \le 10^6 1ai106 范围不大,因此我们可以预处理 1 1 1 ~ 1 0 6 10^6 106 所有数 a i a_i ai 变换后的值 h i h_i hi (先找出质因数,然后看质因数的指数模 2 2 2 后是否为 1 1 1,若为 1 1 1 h i h_i hi 乘上该质因数, h i h_i hi 初始值为 1 1 1),此时对于每个数 a i a_i ai,其质因数 j j j 循环条件为 j ∗ j ≤ a i j * j ≤ a_i jjai,因为 a i a_i ai 是不断变化的( a i a_i ai 不断除质因数),因此处理每个数 a i a_i ai 的时间复杂度为 O ( l o g a i ) \mathcal{O}(loga_i) O(logai),注意如果最终 a i a_i ai 不为 1 1 1 的话表示最终的 a i a_i ai 为质因子,此时 h i h_i hi 还需要乘上 a i a_i ai A C AC AC 时间为 826 m s 826ms 826ms
  • 做法 2 (做法 1 的优化): 在找质因数时,我们可以首先筛出 2 2 2 ~ 1 0 6 10^6 106 的素数,然后用素数 p r i m e j prime_j primej 替代做法 1 1 1 中的 j j j A C AC AC 时间为 498 m s 498ms 498ms
  • 做法 3 : 先使用 E r a t o s t h e n e s Eratosthenes Eratosthenes 筛法预处理出 2 2 2 ~ 1 0 6 10^6 106 每个数的最小质因子 m i n p i minp_i minpi,复杂度为 O ( ∑ 质 数 p ≤ N N P ) = O ( N l o g l o g N ) \mathcal{O}(\sum_{质数p ≤ N} \frac{N}{P})=\mathcal{O}(NloglogN) O(pNPN)=O(NloglogN), 然后在线处理出每个 a i a_i ai 变换后的值 h i h_i hi,在线处理的过程如下: a i a_i ai 除以它的最小质因子 p p p 直到最小质因子不为 p p p,然后再除以新 a i a_i ai 的最小质因子,如此这个过程直到 a i a_i ai 不大于 1 1 1,处理每个 a i a_i ai 的时间复杂度为 O ( l o g a i ) \mathcal{O}(loga_i) O(logai) A C AC AC 时间为 358 m s 358ms 358ms

AC Codes :

做法 2 (注释部分为做法 1):

#include <iostream>
#include <vector>
#include <queue>
#include <stack>
#include <set>
#include <map>
//#include <unordered_set>
//#include <unordered_map>
#include <deque>
#include <list>
#include <iomanip>
#include <algorithm>
#include <fstream>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <assert.h>
using namespace std;
typedef long long ll;
//cout << fixed << setprecision(2);
//cout << setw(2);
const int N = 2e5 + 6, M = 1e9 + 7, INF = 0x3f3f3f3f;

int main() {
    //freopen("/Users/xumingfei/Desktop/ACM/test.txt", "r", stdin);
    ios::sync_with_stdio(false);
    cin.tie(0), cout.tie(0);

    const int MAX = (int) 1e6 + 5;
    vector<int> prime(100000), vis(MAX);
    int cnt = 0;
    for (int i = 2; i < MAX; i++) {
        if (!vis[i]) prime[cnt++] = i;
        for (int j = 0; j < cnt && i * prime[j] < MAX; j++) {
            vis[i * prime[j]] = 1;
            if (i % prime[j] == 0) break;
        }
    }
    vector<int> h(MAX, 1);
    for (int i = 1; i < MAX; i++) {
        int tmp = i;
        for (int j = 0; j < cnt && prime[j] * prime[j] <= tmp; j++) {
            if (tmp % prime[j] == 0) {
                int cnt = 0;
                while (tmp % prime[j] == 0) {
                    tmp /= prime[j];
                    cnt++;
                }
                h[i] *= cnt % 2 ? prime[j] : 1;
            }
        }
        if (tmp > 1) h[i] *= tmp;
    }
    // for (int i = 1; i < MAX; i++) {
    //     int tmp = i;
    //     for (int j = 2; j * j <= tmp; j++) {
    //         if (tmp % j == 0) {
    //             int cnt = 0;
    //             while (tmp % j == 0) {
    //                 tmp /= j;
    //                 cnt++;
    //             }
    //             h[i] *= cnt % 2 ? j : 1;
    //         }
    //     }
    //     if (tmp > 1) h[i] *= tmp;
    // }
    int t;
    cin >> t;
    while (t--) {
        int n;
        cin >> n;
        vector<int> a(n);
        for (int i = 0; i < n; i++) {
            cin >> a[i];
            a[i] = h[a[i]];
        }
        sort(a.begin(), a.end());
        int ans0 = 0, ans1 = 0;
        for (int l = 0; l < n; ) {
            int r = l;
            while (r < n && a[l] == a[r]) {
                r++;
            }
            ans0 = max(ans0, r - l);
            if ((r - l) % 2 == 0 || a[l] == 1)
                ans1 += r - l;
            l = r;
        }
        ans1 = max(ans0, ans1);
        int q;
        cin >> q;
        while (q--) {
            ll w;
            cin >> w;
            cout << (w == 0 ? ans0 : ans1) << '\n';
        }
    }

    return 0;
}

做法 3 :

#include <iostream>
#include <vector>
#include <queue>
#include <stack>
#include <set>
#include <map>
//#include <unordered_set>
//#include <unordered_map>
#include <deque>
#include <list>
#include <iomanip>
#include <algorithm>
#include <fstream>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <assert.h>
using namespace std;
typedef long long ll;
//cout << fixed << setprecision(2);
//cout << setw(2);
const int N = 1e6 + 6, M = 1e9 + 7, INF = 0x3f3f3f3f;

int main() {
    //freopen("/Users/xumingfei/Desktop/ACM/test.txt", "r", stdin);
    ios::sync_with_stdio(false);
    cin.tie(0), cout.tie(0);

    vector<int> minp(N + 1);
    for (int i = 2; i <= N; i++) {
        if (minp[i] == 0) {
            minp[i] = i;
            for (int j = 2 * i; j <= N; j += i) {
                if (minp[j] == 0) {
                    minp[j] = i;
                }
            }
        }
    }
    int t;
    cin >> t;
    while (t--) {
        int n;
        cin >> n;
        vector<int> a(n);
        for (int i = 0; i < n; i++) {
            cin >> a[i];
        }
        for (int i = 0; i < n; i++) {
            int x = a[i];
            a[i] = 1;
            while (x > 1) {
                int q = minp[x];
                int cnt = 0;
                while (minp[x] == q) {
                    cnt++;
                    x /= q;
                }
                if (cnt % 2 == 1) {
                    a[i] *= q;
                }
            }
        }
        sort(a.begin(), a.end());
        int ans0 = 0, ans1 = 0;
        for (int l = 0; l < n; ) {
            int r = l;
            while (r < n && a[l] == a[r]) {
                r++;
            }
            ans0 = max(ans0, r - l);
            if ((r - l) % 2 == 0 || a[l] == 1)
                ans1 += r - l;
            l = r;
        }
        ans1 = max(ans0, ans1);
        int q;
        cin >> q;
        while (q--) {
            ll w;
            cin >> w;
            cout << (w == 0 ? ans0 : ans1) << '\n';
        }
    }

    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值