1. 程式人生 > 其它 >Codeforces Round694 Div2

Codeforces Round694 Div2

技術標籤:Codeforces數學

Codeforces Round694 Div2

D Strange Definition

D題沒做出來。題意也比較複雜,n個數 a 1 , a 2 , ⋯   , a n a_1, a_2, \cdots, a_n a1,a2,,an。先上一個定義,如果正整數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)是完全平方數,那麼x和y鄰近。每一時刻, a i a_i ai會變成陣列中其他所有和它鄰近的元素之積(包括自己,如果自己和自己鄰近)。有 q q

q次查詢,每次查詢問 w w w個時刻後的陣列中,設 d i d_i di表示和 a i a_i ai鄰近的元素數量(包括自己),問最大的 d i d_i di是多少?其中 n n n 1 0 5 10^5 105量級, w w w 1 0 18 10^{18} 1018量級, a i a_i ai 1 0 6 10^6 106量級, q q q 1 0 5 10^5 105量級。

樣例輸入:

2
4
6 8 4 2
1
0
6
12 3 20 5 80 1
1
1

樣例輸出:

2
3

比如樣例,{6,8,4,2}裡,8和8,2相鄰所以最大是2。第二個樣例,1秒後,陣列變成{36,36,8000,8000,8000,1},d最大的是8000,是3。

首先肯定是化簡鄰近的意義,設 g c d ( x , y ) = k gcd(x,y)=k gcd(x,y)=k,那麼 l c m ( x , y ) g c d ( x , y ) = x y k 2 \frac{lcm(x,y)}{gcd(x,y)}=\frac{xy}{k^2} gcd(x,y)lcm(x,y)=k2xy,開根號之後說明xy要是完全平方數。想了一會會不會 x y \sqrt{xy} xy 不整除k,答案是不可能,因為x和y都整除k。所以鄰近等價於 x y xy xy是完全平方數。

比賽時想到這個,後來就沒什麼辦法了。其實需要繼續挖掘這個條件,將x和y質因數分解之後,相乘如果是完全平方數,肯定是相乘之後所有質因數的次數都是偶數。換言之,對於一個數 a i a_i

ai,可以約掉所有偶數次的質因數冪,比如說第二個例子, 12 = 3 ⋅ 2 2 12=3\cdot 2^2 12=322,直接變成3。 20 = 5 ⋅ 2 2 20=5\cdot 2^2 20=522 80 = 5 ⋅ 2 4 80=5\cdot 2^4 80=524,都可以直接變成5。x和y鄰近,現在等價於,這樣約了之後相等的元素,因為偶數次冪不用管,奇數次冪,需要另一個數中也有這個奇數次冪才能開根號開掉。

所以經過這個處理, a a a陣列變成等價之後的結果, w = 0 w=0 w=0時最大的d,其實就是等價之後出現次數最多的數。那 w > 0 w>0 w>0怎麼辦?

我們看第一秒。第一秒等價於,比方說 b i b_i bi是化簡後的值, a i a_i ai會變成數組裡所有的 b i b_i bi的乘積。比如說第二個樣例,化簡之後陣列變成{3,3,5,5,5,1},第一秒後變成 { 3 2 , 3 2 , 5 3 , 5 3 , 5 3 , 1 } \{3^2,3^2,5^3,5^3,5^3,1\} {32,32,53,53,53,1},這個時候,如果一個等價的集合有偶數個,比如3,那麼和他鄰近的數有所有其他的偶數冪,以及1(不能忘了1)。而對於一個奇數次冪,和他鄰近的,只有值和他自己相等的數。比如這裡, 3 2 3^2 32的d是3, 5 3 5^3 53的d也是3。所以 w = 1 w=1 w=1時候的思路是,比較最大的奇數次冪 個數,和所有偶數次冪和1加起來的次數,取max值,就是新的 d m a x d_{max} dmax

那接下來怎麼辦?我們繼續看 w = 2 w=2 w=2,可以發現乘了之後,樣例2變成了 { 3 4 , 3 4 , 5 9 , 5 9 , 5 9 , 3 4 } \{3^4,3^4,5^9,5^9,5^9,3^4\} {34,34,59,59,59,34},所有的偶數次冪和1合併成了一個群體,而奇數次冪仍然不變。偶數次冪合併之後還是偶數次冪,奇數次冪演變之後還是奇數次冪,所以接下來的變化還是偶數次冪合併,奇數次冪只乘和自己等的冪。所以接下來的d永遠不會變化。這也是為什麼 w w w這麼大。程式碼如下:

  • 質因數分解的技巧,先預處理每個數的minFactor
  • mp儲存每個 a i a_i ai等價之後的結果,用奇數次冪的質因數之積表示
  • 答案分為兩種,一種是w=0,一種是w>0
  • 突然發現even和odd好像寫反了。。
//
//  main.cpp
//
//
//  Created by ji luyang on 2020/12/22.
//

#include <stdio.h>
#include <iostream>
#include <fstream>
#include <string>
#include <cstring>
#include <time.h>
#include <algorithm>
#include <vector>
#include <queue>
#include <map>
using namespace std;
int t;
int a[300010], minFactor[1000010];
int main() {
    memset(minFactor, 0, sizeof(minFactor));
    for (int i = 2; i <= 1e6; i++) {
        if (minFactor[i] == 0) {
            minFactor[i] = i;
            for (int j = 2 * i; j <= 1e6; j += i) {
                if (minFactor[j] == 0) {
                    minFactor[j] = i;
                }
            }
        }
    }
    cin >> t;
    while (t--) {
        int n, q;
        cin >> n;
        map<int, int> mp;
        for (int i = 1; i <= n; i++) {
            scanf("%d", &a[i]);
            int x = a[i], y = 1;
            while (x > 1) {
                int minp = minFactor[x], cnt = 0;
                while (x % minp == 0) {
                    x /= minp;
                    cnt++;
                }
                if (cnt % 2) {
                    y *= minp;
                }
            }
            mp[y]++;
        }
        int res0 = 0, res1 = 0, cntodd = 0, maxeven = 0;
        for (auto it: mp) {
            res0 = max(res0, it.second);
            if (it.first == 1 || it.second % 2 == 0) {
                cntodd += it.second;
            } else {
                maxeven = max(maxeven, it.second);
            }
        }
        res1 = max(cntodd, maxeven);
        cin >> q;
        while (q--) {
            long long w;
            scanf("%lld", &w);
            if (!w) printf("%d\n", res0);
            else printf("%d\n", res1);
        }
    }
    
    return 0;
}
 

F Strange Housing

n個點,m條邊。老師們要住在一些點裡,要滿足一些條件:

  • 如果一條邊的兩個頂點都沒有老師,這條邊會刪掉
  • 刪掉邊之後,整個圖要是聯通的
  • 老師不能住在一條邊的兩個頂點

n是 1 0 5 10^5 105級別,m也是。計算是否存在這樣的點集?如果存在,輸出任意的點集。

樣例輸入:

2
3 2
3 2
2 1
4 2
1 4
2 3

樣例輸出:

YES
2
1 3 
NO

樣例輸入:

1
17 27
1 8
2 9
3 10
4 11
5 12
6 13
7 14
8 9
8 14
8 15
9 10
9 15
10 11
10 15
10 17
11 12
11 17
12 13
12 16
12 17
13 14
13 16
14 16
14 15
15 16
15 17
16 17

樣例輸出:

YES
8
1 3 4 5 6 9 14 17 

在這裡插入圖片描述
其實就是染色問題。比如從點1開始,染成黑色,那麼其他和它相連的點都得染成白色,即黑色不能相連。也不能有一個白點連的所有點都是白色,否則這個點將不可達。所以邊dfs邊染色,最後的黑色點集即為所求:

  • 對於一個點,先檢查是否連有黑點。如果有,一定染成白色;如果沒有,那就染成黑色
  • 要注意一個問題:這裡如果每個t內,用memset去初始化color會超時。memset也是要對這麼多位去置0的,有的樣例t很大,但是每個樣例內的m和n很小,這種情況就會超時。
//
//  main.cpp
//
//
//  Created by ji luyang on 2020/12/22.
//

#include <stdio.h>
#include <iostream>
#include <fstream>
#include <string>
#include <cstring>
#include <time.h>
#include <algorithm>
#include <vector>
#include <queue>
#include <map>
using namespace std;
int t;
vector<int> g[300010];
int n, m;
int color[300010];
void dfs(int u) {
    int flag = 1;
    for (int v: g[u]) {
        if (color[v] == 1) {
            flag = 0;
            break;
        }
    }
    if (flag) color[u] = 1;
    else color[u] = -1;
    for (int v: g[u]) {
        if (color[v] == 0) {
            dfs(v);
        }
    }
}
int main() {
    cin >> t;
    while (t--) {
        for (int i = 1; i <= n; i++) {
            g[i].clear();
            color[i] = 0;
        }
        scanf("%d %d", &n, &m);
        for (int i = 0; i < m; i++) {
            int u, v;
            scanf("%d %d", &u, &v);
            g[u].push_back(v);
            g[v].push_back(u);
        }
        dfs(1);
        int ifconnect = 1, sum = 0;
        for (int i = 1; i <= n; i++) {
            if (!color[i]) ifconnect = 0;
            if (color[i] == 1) sum++;
        }
        if (!ifconnect) printf("NO\n");
        else {
            printf("YES\n");
            printf("%d\n", sum);
            for (int i = 1; i <= n; i++) {
                if (color[i] == 1) {
                    printf("%d ", i);
                }
            }
            printf("\n");
        }
    }
    
    return 0;
}