牛牛的最大興趣組

SSL_TJH發表於2020-10-18

牛牛的最大興趣組 ⁡ \operatorname{牛牛的最大興趣組}

題目連結: nowcoder 212979 ⁡ \operatorname{nowcoder\ 212979} nowcoder 212979

題目

牛牛的班級中有 n n n 個人,他們的性格各不相同。
牛牛現在想要從這 n n n 個人中選出一些人組成一個興趣小組,但是他想讓參加這個興趣小組的人數儘可能的多。但是他有不想讓其中有任何一對人之間由於性格問題產生矛盾。
具體來說,如果這個興趣小組中出現兩個人性格值的乘積開三次方根是一個正整數,就認為他們兩個性格不合。
比如一個性格值為 2 2 2 的同學和一個性格值為4的同學就是性格不合的,因為 2 × 4 = 8 2\times 4=8 2×4=8,而一個性格值為 2 2 2 的同學和一個性格值為 8 8 8 的同學性格相合,可以出現在同一個興趣小組中,因為 2 × 8 = 16 2\times 8=16 2×8=16 16 16 16 開三次方根不是一個正整數。

請你告訴牛牛,他們班的同學組成的最大興趣小組的人數是多少。

輸入

第一行輸入一個正整數 n n n 表示牛牛所在的班級中的人數。
接下來輸入一行 n n n 個正整數 a i a_i ai 表示每個人的性格值。

輸出

輸出一行一個正整數,表示最大興趣小組的人數。

樣例輸入

4
4 2 16 27

樣例輸出

3

樣例解釋

1號和2號同學性格值的乘積為 8 = 2 3 8=2^3 8=23,性格不合,1號和3號同學性格值的乘積為 64 = 4 3 64=4^3 64=43,性格不合。
選取第2,3,4號同學組成一個最大興趣組,共 3 3 3 人。

資料範圍

對於 10 % 10\% 10% 的測試資料,保證 1 ≤ n ≤ 10 , 1 ≤ a i ≤ 500 1 \leq n \leq 10,1 \leq a_i \leq 500 1n10,1ai500
對於 20 % 20\% 20% 的測試資料,保證 1 ≤ n ≤ 10 , 1 ≤ a i ≤ 1 0 9 1 \leq n \leq 10,1 \leq a_i \leq 10^9 1n10,1ai109
對於 30 % 30\% 30% 的測試資料,保證 1 ≤ n ≤ 150 , 1 ≤ a i ≤ 2 × 1 0 9 1 \leq n \leq 150,1 \leq a_i \leq 2 \times 10^9 1n150,1ai2×109
對於 40 % 40\% 40% 的測試資料,保證 1 ≤ n ≤ 1000 , 1 ≤ a i ≤ 2 × 1 0 9 1 \leq n \leq 1000,1 \leq a_i \leq 2 \times 10^9 1n1000,1ai2×109
對於 100 % 100\% 100% 的測試資料,保證 1 ≤ n ≤ 1 0 5 , 1 ≤ a i ≤ 2 × 1 0 9 1 \leq n \leq 10^5,1 \leq a_i \leq 2 \times 10^9 1n105,1ai2×109

思路

這道題是一道數學題。

要讓兩個數乘積是某個整數的三次方,就是要讓他們的積的質因數分解出來的質數中,每個質數的個數一定是 3 3 3 的倍數。

那我們可以考慮這樣先處理一下,把每一個有次方數作為因子的數都除掉所有的次方數,直到沒有。

舉個例子: 27 27 27 在處理完之後會變成 1 1 1 16 16 16 在處理完之後會變成 2 2 2 9 9 9 在處理完之後會變成 9 9 9

那我們會發現,要讓處理完的某兩個數乘起來是某個數的三次方,這兩個數是一一對應的。
(注意:這裡的一一對應是處理完的數,不是處理之前的數)
為什麼呢?

  1. A數有 0 0 0 個質因子 x x x,那B數要跟ta對應,就必須要有 0 0 0 個質因子 x x x
  2. A數有 1 1 1 個質因子 x x x,那B數要跟ta對應,就必須要有 2 2 2 個質因子 x x x
  3. A數有 2 2 2 個質因子 x x x,那B數要跟ta對應,就必須要有 1 1 1 個質因子 x x x
  • A數或B數有更多的這個質因子?不可能,因為如果有,也會被前面的處理除掉

那我們就可以通過這個一一對應,知道A這一類的和B這一類的數不能同時存在。
那到底留下那一邊呢?簡單,就是留多的那一邊,因為答案要求留下的數更多。

有一個要注意的就是:如果數處理完之後就變成了 1 1 1,就要單獨處理,因為 1 1 1 是跟自己對應的。

考試時

想到了要處理數字,但是沒有想到處理完的數字是一一對應的,就只能唯唯諾諾的打了個 n^2 30分程式碼。
在這裡插入圖片描述

程式碼

#include<map>
#include<cstdio>
#include<iostream>

#define ll long long

using namespace std;

ll n, x, nxt[100001], num[100001], from, to, tot, one, ans;
map <int, int> pd;
bool use[100001];

int main() {
    scanf("%lld", &n);
    for (ll i = 1; i <= n; i++) {
        scanf("%lld", &x);
        
        from = x;//最後求出原來的數除以所有立方數因子的積
        to = 1;//最後求出來是要讓 from 可以不和要乘上什麼
        //注意下面這裡範圍是 j^2 <= x 不是 j^3 <= x
        for (ll j = 2; j * j <= x; j++) {
            if (x % j) continue;
            while (x % (j * j * j) == 0) {
                x /= (j * j * j);
                from /= (j * j * j);
            }
            if (x % (j * j) == 0) {
                x /= (j * j);
                to *= j;//from 有兩個,一共要三個,所以這裡要給一個
            }
            else if (x % j == 0) {
                x /= j;
                to *= (j * j);//from 有一個,一共要三個,所以這裡要給兩個
            }
        }
        if (x > 1) to *= (x * x);//剩下一個素數,還要補兩個
        
        if (from == 1) {//特殊處理只由立方數構成的數
            one = 1;
            continue;
        }
        
        if (!pd[from]) {//配對
            num[++tot] = 1;
            pd[from] = tot;
        }
        else num[pd[from]]++;
        if (!pd[to]) pd[to] = ++tot;
        nxt[pd[from]] = pd[to];//記錄另一半的編號
    }
    
    for (ll i = 1; i <= tot; i++)
        if (!use[i]) {
            ans += max(num[i], num[nxt[i]]);
            //對於每一對,把少的一邊全部不要
            use[i] = 1;
            use[nxt[i]] = 1;
        }
    
    printf("%lld", ans + one);//輸出(one 就是上面的特殊處理)
    
    return 0;
}

相關文章