Codeforces Round 985 div1+2 個人題解(A~E)

ExtractStars發表於2024-11-13

Codeforces Round 985 div1+2 個人題解(A~E)

Dashboard - Refact.ai Match 1 (Codeforces Round 985) - Codeforces

火車頭

#include <bits/stdc++.h>

using namespace std;

#define ft first
#define sd second

#define yes cout << "yes\n"
#define no cout << "no\n"

#define Yes cout << "Yes\n"
#define No cout << "No\n"

#define YES cout << "YES\n"
#define NO cout << "NO\n"

#define pb push_back
#define eb emplace_back

#define all(x) x.begin(), x.end()
#define all1(x) x.begin() + 1, x.end()
#define unq_all(x) x.erase(unique(all(x)), x.end())
#define unq_all1(x) x.erase(unique(all1(x)), x.end())
#define inf 0x3f3f3f3f
#define infll 0x3f3f3f3f3f3f3f3fLL

#define RED cout << "\033[91m"     // 紅色
#define GREEN cout << "\033[92m"   // 綠色
#define YELLOW cout << "\033[93m"  // 藍色
#define BLUE cout << "\033[94m"    // 品紅
#define MAGENTA cout << "\033[95m" // 青色
#define CYAN cout << "\033[96m"    // 青色
#define RESET cout << "\033[0m"    // 重置

typedef long long ll;
typedef unsigned long long ull;
typedef long double ld;
// typedef __int128_t i128;

typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
typedef pair<ll, int> pli;
typedef pair<string, ll> psl;

typedef tuple<int, int, int> ti3;
typedef tuple<ll, ll, ll> tl3;
typedef tuple<ld, ld, ld> tld3;

typedef vector<bool> vb;
typedef vector<int> vi;
typedef vector<ll> vl;
typedef vector<string> vs;
typedef vector<vi> vvi;
typedef vector<vl> vvl;

// std::mt19937_64 rng(std::chrono::steady_clock::now().time_since_epoch().count());

template <typename T>
inline T read()
{
    T x = 0;
    int y = 1;
    char ch = getchar();
    while (ch > '9' || ch < '0')
    {
        if (ch == '-')
            y = -1;
        ch = getchar();
    }
    while (ch >= '0' && ch <= '9')
    {
        x = (x << 3) + (x << 1) + (ch ^ 48);
        ch = getchar();
    }
    return x * y;
}

template <typename T>
inline void write(T x)
{
    if (x < 0)
    {
        putchar('-');
        x = -x;
    }
    if (x >= 10)
    {
        write(x / 10);
    }
    putchar(x % 10 + '0');
}

/*#####################################BEGIN#####################################*/
void solve()
{
}

int main()
{
    ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0);
    // freopen("test.in", "r", stdin);
    // freopen("test.out", "w", stdout);
    int _ = 1;
    std::cin >> _;
    while (_--)
    {
        solve();
    }
    return 0;
}

/*######################################END######################################*/
// 連結:

A. Set

給你一個正整數 \(k\) 和一個由 \(l\)\(r\) (含)的所有整陣列成的集合 \(S\)

您可以執行以下兩步運算的任意次數(可能為零):

首先,從集合 \(S\) 中選擇一個數字 \(x\),使得 \(S\) 中至少有 \(k\)\(x\) 的倍數(包括 \(x\) 本身);
然後,從 \(S\) 中刪除 \(x\) (注意沒有刪除任何其他內容)。
求可以進行的最大運算元。

輸入
每個測試包含多個測試用例。輸入的第一行包含一個整數 \(t\)\(1 \leq t \leq 10^4\))—— 測試用例的數量。測試用例說明如下。

每個測試用例的唯一一行包含三個整數 \(l\)\(r\)\(k\)\(1 \leq l \leq r \leq 10^9\)\(1 \leq k \leq r - l + 1\))—— 最小整數 \(S\)、最大整數 \(S\) 和引數 \(k\)

輸出
對於每個測試用例,輸出一個整數——可執行的最大運算元。

示例
輸入

8
3 9 2
4 9 1
7 9 2
2 10 2
154 220 2
147 294 2
998 24435 3
1 1000000000 2

輸出

2
6
0
4
0
1
7148
500000000

提示
在第一個測試用例中,初始時 \(S=\{3,4,5,6,7,8,9\}\)。一個可能的最佳操作序列是:

選擇 \(x=4\) 進行第一次操作,因為 \(S\) 中有兩個 \(4\) 的倍數:\(4\)\(8\)\(S\) 變為 \(\{3,5,6,7,8,9\}\)
選擇 \(x=3\) 進行第二次操作,因為 \(S\) 中有三個 \(3\) 的倍數:\(3\)\(6\)\(9\)\(S\) 變為 \(\{5,6,7,8,9\}\)

在第二個測試用例中,初始時 \(S=\{4,5,6,7,8,9\}\)。一個可能的最佳操作序列是:

選擇 \(x=5\)\(S\) 變為 \(\{4,6,7,8,9\}\)
選擇 \(x=6\)\(S\) 變為 \(\{4,7,8,9\}\)
選擇 \(x=4\)\(S\) 變為 \(\{7,8,9\}\)
選擇 \(x=8\)\(S\) 變為 \(\{7,9\}\)
選擇 \(x=7\)\(S\) 變為 \(\{9\}\)
選擇 \(x=9\)\(S\) 變為空集。

在第三個測試用例中,初始時 \(S=\{7,8,9\}\)。對於 \(S\) 中的每個 \(x\),無法找到除 \(x\) 本身以外的其他 \(x\) 的倍數。由於 \(k=2\),您無法進行任何操作。

在第四個測試用例中,初始時 \(S=\{2,3,4,5,6,7,8,9,10\}\)。一個可能的最佳操作序列是:

選擇 \(x=2\)\(S\) 變為 \(\{3,4,5,6,7,8,9,10\}\)
選擇 \(x=4\)\(S\) 變為 \(\{3,5,6,7,8,9,10\}\)
選擇 \(x=3\)\(S\) 變為 \(\{5,6,7,8,9,10\}\)
選擇 \(x=5\)\(S\) 變為 \(\{6,7,8,9,10\}\)

解題思路

對於一個數\(n\),我們能構造出的最大的有 \(k\)\(x\) 的倍數的\(x\)\(\lfloor \frac{n}{k}\rfloor\)。因此,答案為\(\max(0,\lfloor \frac{r}{k}\rfloor-l+1)\)

實現程式碼

void solve()
{
    ll l, r, k;
    cin >> l >> r >> k;
    cout << max(0ll, r / k - l + 1) << "\n";
}

B. Replacement

您有一個長度為 \(n\) 的二進位制字串 \(s\),而艾瑞絲會給出另一個長度為 \(n-1\) 的二進位制字串 \(r\)

艾瑞絲將和你玩一個遊戲。在遊戲過程中,你將對 \(s\) 執行 \(n-1\) 操作。在第 \(i\) 次操作中(\(1 \leq i \leq n-1\)):

首先,你要選擇一個索引 \(k\),使得 \(1 \leq k \leq |s| - 1\)\(s_k \neq s_{k+1}\)。如果無法選擇這樣的索引,那麼就輸了;
然後,將 \(s_k s_{k+1}\) 替換為 \(r_i\)。請注意,這會使 \(s\) 的長度減少 \(1\)
如果所有的 \(n-1\) 操作都成功執行,那麼你就贏了。

請判斷你是否有可能贏得這個遊戲。

輸入
每個測試包含多個測試用例。輸入的第一行包含一個整數 \(t\)\(1 \leq t \leq 10^4\))—— 測試用例的數量。測試用例說明如下。

每個測試用例的第一行包含一個整數 \(n\)\(2 \leq n \leq 10^5\))—— \(s\) 的長度。

第二行包含長度為 \(n\)\(s_i = 0\)\(1\))的二進位制字串 \(s\)

第三行包含長度為 \(n-1\)\(r_i = 0\)\(1\))的二進位制字串 \(r\)

保證所有測試用例中 \(n\) 的總和不超過 \(10^5\)

輸出
對於每個測試用例,如果能贏得遊戲,則列印 "YES"(不帶引號),否則列印 "NO"(不帶引號)。

您可以用任何大小寫(大寫或小寫)輸出答案。例如,字串 "yEs"、"yes"、"Yes" 和 "YES" 將被識別為肯定回答。

示例
輸入

6
2
11
0
2
01
1
4
1101
001
6
111110
10000
6
010010
11010
8
10010010
0010010

輸出

NO
YES
YES
NO
YES
NO

提示
在第一個測試用例中,您無法執行第一次操作。因此,您輸了遊戲。

在第二個測試用例中,您可以選擇 \(k=1\) 進行唯一的操作,之後 \(s\) 變為 \(1\)。因此,您贏得了遊戲。

在第三個測試用例中,您可以執行以下操作:\(110 \to r_1 = 0101 \to r_2 = 010 \to r_3 = 11\)

解題思路

注意到我們每次是選擇一個\(01\)或者\(10\)來進行替換,使其留下\(1\)或者\(0\),因此,每個替換都等價於刪除另一種數字,例如替換為\(1\)等價於刪除一個\(0\),所以,我們可以維護\(0\)\(1\)的數量,如果在最後一次刪除前\(0\)\(1\)的數量歸零則遊戲輸了。

實現程式碼

void solve()
{
    int n;
    string s, r;
    cin >> n >> s >> r;
    int cnt0 = 0;
    int cnt1 = 0;
    for (auto c : s)
    {
        if (c == '1')
            cnt1++;
        else
            cnt0++;
    }
    if (cnt0 == 0 || cnt1 == 0)
    {
        NO;
        return;
    }
    for (int i = 0; i < n - 1; i++)
    {
        if (r[i] == '1')
            cnt0--;
        else
            cnt1--;
        if (i == n - 2)
            continue;
        if (cnt0 == 0 || cnt1 == 0)
        {
            NO;
            return;
        }
    }
    YES;
}

C. New Rating

你好,Codeforcescode!

凱文曾經是 Codeforces 的參與者。最近,KDOI 團隊開發了一個名為 Forcescode 的新線上裁判。

凱文參加過 Forcescode 上的 \(n\) 場比賽。在第 \(i\) 場比賽中,他的表現評分為 \(a_i\)

現在他黑進了 Forcescode 的後臺,將選擇一個時間間隔 \([l,r]\)\(1 \leq l \leq r \leq n\)),然後跳過這個時間間隔內的所有比賽。之後,他的評分將按以下方式重新計算:

最初,他的評分為 \(x=0\)
每次 \(1 \leq i \leq n\),在第 \(i\) 場比賽之後:
如果 \(l \leq i \leq r\),則跳過這場比賽,等級分保持不變;
否則,他的評級將根據以下規則更新:
如果 \(a_i > x\),他的評分 \(x\) 將增加 \(1\)
如果 \(a_i = x\),他的評分 \(x\) 將保持不變;
如果 \(a_i < x\),他的評分 \(x\) 將減少 \(1\)
如果凱文以最佳方式選擇了區間 \([l,r]\),您必須幫助他找到重新計算後的最大可能評分。注意凱文至少要跳過一次比賽。

輸入
每個測試包含多個測試用例。輸入的第一行包含一個整數 \(t\)\(1 \leq t \leq 5 \cdot 10^4\))—— 測試用例的數量。測試用例說明如下。

每個測試用例的第一行包含一個整數 \(n\)\(1 \leq n \leq 3 \cdot 10^5\))—— 競賽次數。

第二行包含 \(n\) 個整數 \(a_1, a_2, \ldots, a_n\)\(1 \leq a_i \leq n\))—— 競賽中的效能評級。

保證所有測試用例中 \(n\) 的總和不超過 \(3 \cdot 10^5\)

輸出
對於每個測試用例,輸出一個整數——如果凱文以最佳方式選擇間隔,重新計算後可能的最大評級。

示例
輸入

5
6
1 2 3 4 5 6
7
1 2 1 1 1 3 4
1
1
9
9 9 8 2 4 4 3 5 3
10
1 2 3 4 1 3 2 1 1 10

輸出

5
4
0
4
5

提示
在第一個測試用例中,凱文必須跳過至少一場比賽。如果他選擇任何長度為 \(1\) 的區間,他的評分在重新計算後將等於 \(5\)

在第二個測試用例中,凱文的最佳選擇是選擇區間 \([3,5]\)。在重新計算期間,他的評分變化如下:

\(0 \to a_1=1 \to a_2=2 \to \text{跳過}2 \to \text{跳過}2 \to \text{跳過}2 \to a_6=3 \to a_7=4\)

在第三個測試用例中,凱文必須跳過唯一的比賽,因此他的評分將保持在初始值 \(0\)

在第四個測試用例中,凱文的最佳選擇是選擇區間 \([7,9]\)。在重新計算期間,他的評分變化如下:

\(0 \to a_1=9 \to a_2=9 \to a_3=8 \to a_4=2 \to a_5=4 \to a_6=4 \to \text{跳過}4 \to \text{跳過}4 \to \text{跳過}4\)

在第五個測試用例中,凱文的最佳選擇是選擇區間 \([5,9]\)

解題思路

我們可以設計三個\(dp\)\(f,g,h\)\(f[i]\)代表第\(i\)次比賽不進行任何跳過的評分,\(g[i]\)代表第\(i\)次比賽正在進行跳過的最大評分,\(h[i]\)代表第\(i\)次比賽已經進行跳過後的最大平方,我們令\(calc(a[i])\)代表評級的更新規則,則可以得到以下狀態轉移方程:\(f[i]=f[i-1]+clac(a[i]),g[i]=\max(g[i-1],f[i]),h[i]=\max(h[i-1]+calc(a[i]),g[i-1])\)

實現程式碼

void solve()
{
    int n;
    cin >> n;
    vi a(n + 1);
    for (int i = 1; i <= n; i++)
    {
        cin >> a[i];
    }
    vi f(n + 1, -inf);
    vi g(n + 1, -inf);
    vi h(n + 1, -inf);
    f[0] = g[0] = h[0] = f[1] = h[1] = 0;
    for (int i = 1; i <= n; i++)
    {
        int v = a[i];
        if (f[i - 1] < v)
            f[i] = f[i - 1] + 1;
        else if (f[i - 1] == v)
            f[i] = f[i - 1];
        else
            f[i] = f[i - 1] - 1;

        g[i] = max(f[i], g[i - 1]);
        if (i == 1)
            continue;
        if (h[i - 1] < v)
            h[i] = h[i - 1] + 1;
        else if (h[i - 1] == v)
            h[i] = h[i - 1];
        else
            h[i] = h[i - 1] - 1;

        h[i] = max(h[i], g[i - 1]);
    }
    cout << h[n] << endl;
}

D. Cool Graph

給你一個無向圖,其中有 \(n\) 個頂點和 \(m\) 條邊。

您最多可以執行以下操作 \(2 \cdot \max(n,m)\) 次:

選擇三個不同的頂點 \(a\)\(b\)\(c\),然後對每條邊 \((a,b)\)\((b,c)\)\((c,a)\) 執行以下操作:
如果該邊不存在,則新增該邊。相反,如果存在,則刪除。
當且僅當以下條件之一成立時,圖才被稱為酷圖:

該圖沒有邊,或
該圖是一棵樹。
您必須透過執行上述操作使圖形冷卻。請注意,您最多可以進行 \(2 \cdot \max(n,m)\) 次操作。

可以證明,總是存在至少一個解。

輸入
每個測試包含多個測試用例。第一行輸入包含一個整數 \(t\)\(1 \leq t \leq 10^4\))—— 測試用例的數量。測試用例說明如下。

每個測試用例的第一行包含兩個整數 \(n\)\(m\)\(3 \leq n \leq 10^5\)\(0 \leq m \leq \min\left(\frac{n(n-1)}{2}, 2 \cdot 10^5\right)\))—— 頂點數和邊數。

接著是 \(m\) 行,\(i\) 行包含兩個整數 \(u_i\)\(v_i\)\(1 \leq u_i, v_i \leq n\))—— 第 \(i\) 條邊所連線的兩個節點。

保證所有測試用例中 \(n\) 的總和不超過 \(10^5\),所有測試用例中 \(m\) 的總和不超過 \(2 \cdot 10^5\)

保證給定圖中不存在自迴圈或多重邊。

輸出
對於每個測試用例,在第一行輸出一個整數 \(k\)\(0 \leq k \leq 2 \cdot \max(n,m)\))—— 運算次數。

然後輸出 \(k\) 行,第 \(i\) 行包含三個不同的整數 \(a\)\(b\)\(c\)\(1 \leq a,b,c \leq n\))—— 你在第 \(i\) 次操作中選擇的三個整數。

如果有多個解,可以輸出其中任意一個。

示例
輸入

5
3 0
3 1
1 2
3 2
1 2
2 3
3 3
1 2
2 3
3 1
6 6
1 2
1 6
4 5
3 4
4 6
3 6

輸出

0
1
1 2 3
0
1
1 2 3
3
1 3 6
2 4 5
3 4 6

提示
在第一個測試用例中,圖已經是酷圖,因為沒有邊。

在第二個測試用例中,執行唯一的操作後,圖變成了一棵樹,因此是酷圖。

在第三個測試用例中,圖已經是酷圖,因為它是一棵樹。

在第四個測試用例中,執行唯一的操作後,圖沒有邊,因此是酷圖。

解題思路

看到限制條件為最多可以進行 \(2 \cdot \max(n,m)\) 次操作,結合樹的邊數為 \(n-1\) ,因此我們可以考慮先將圖刪掉儘可能能多的邊,然後再重新構建出一棵樹,剛好可以接近用完操作。

順著這個思路往下想,對於一個度數大於等於 \(2\) 的點 \(x\) ,我們可以選擇對 \(x\) 和它的兩個鄰接點 \(y\)\(z\) 進行操作,如果 \(y\)\(z\) 存在一條邊,則一次性刪除了三條邊,否則,我們刪除了兩條邊並構建了一條邊 \((y,z)\) ,至少刪除了一條邊。因此,我們最多不會超過 \(m\) 次,就可以將 這個圖刪的只剩下孤點和孤邊。如果沒有孤邊,說明所有邊已被刪除,直接滿足要求。

考慮對一堆孤點和孤邊進行操作來構建一棵樹。

我們可以選取一個孤邊作為樹的基礎。

如果遇見孤點,則對樹的兩個端點 \(x\)\(y\) 以及孤點 \(z\) 進行操作,這樣我們相當於在 \(x\)\(y\) 之間插入了一個點 \(z\) 使得邊 \(x\rarr y\) 變成 \(x \rarr z \rarr y\) ,然後我們把 \(z\) 賦值給 \(y\) 即可。

如果遇見孤邊,則對孤邊的兩個端點 \(u\)\(v\) 和樹的端點 \(x\) 進行操作,這樣我們相當於將 \(u\)\(v\) 與端點 \(x\) 建邊,使得邊 \(u \rarr v ,x\) 變成 \(u \rarr x \larr v\)

實現程式碼

void solve()
{
    int n, m;
    cin >> n >> m;
    vector<set<int>> adj(n + 1);
    for (int i = 0; i < m; i++)
    {
        int u, v;
        cin >> u >> v;
        adj[u].insert(v);
        adj[v].insert(u);
    }
    if (m == 0)
    {
        cout << "0\n";
        return;
    }
    vector<array<int, 3>> ans;
    auto del = [&](int a, int b, int c)
    {
        adj[a].erase(b);
        adj[b].erase(a);
        adj[a].erase(c);
        adj[c].erase(a);
        if (adj[b].find(c) != adj[b].end())
        {
            adj[b].erase(c);
            adj[c].erase(b);
        }
        else
        {
            adj[b].insert(c);
            adj[c].insert(b);
        }
    };
    for (int i = 1; i <= n; i++)
    {
        while (adj[i].size() >= 2)
        {
            int a = i;
            int b = *adj[i].begin();
            int c = *next(adj[i].begin());
            ans.pb({a, b, c});
            del(a, b, c);
        }
    }
    int u = 0, v = 0;
    for (int i = 1; i <= n; i++)
    {
        if (adj[i].size() == 1)
        {
            u = i;
            v = *adj[i].begin();
            break;
        }
    }
    if (u)
    {
        vb vis(n + 1);
        vis[u] = vis[v] = 1;
        for (int i = 1; i <= n; i++)
        {
            if (vis[i])
                continue;
            if (adj[i].size() == 1)
            {
                ans.pb({u, i, *adj[i].begin()});
                vis[i] = 1;
                vis[*adj[i].begin()] = 1;
            }
            else
            {
                ans.pb({u, v, i});
                vis[i] = 1;
                v = i;
            }
        }
    }
    cout << ans.size() << "\n";
    for (auto [a, b, c] : ans)
    {
        cout << a << " " << b << " " << c << "\n";
    }
}

E. Common Generator

對於兩個整數 \(x\)\(y\) (\(x,y \geq 2\)),當且僅當 \(x\) 可以透過執行下面的操作轉換為 \(y\) 時,我們才會說 \(x\)\(y\) 的生成器:

選擇 \(x\) 的除數 \(d\) (\(d \geq 2\)),然後將 \(x\) 增加 \(d\)
例如:

\(3\)\(8\) 的生成器,因為我們可以進行以下運算:\(3 \xrightarrow{d=3} 6 \xrightarrow{d=2} 8\)
\(4\)\(10\) 的生成器,因為我們可以進行以下運算:\(4 \xrightarrow{d=4} 8 \xrightarrow{d=2} 10\)
\(5\) 不是 \(6\) 的生成器,因為我們無法透過上述操作將 \(5\) 轉化為 \(6\)

現在,凱文給你一個陣列 \(a\),由 \(n\) 個成對不同的整數 (\(a_i \geq 2\)) 組成。

你必須找到一個整數 \(x \geq 2\),使得每個 \(1 \leq i \leq n\) 的生成數 \(x\) 都是 \(a_i\) 的生成數,或者確定這樣的整數不存在。

輸入
每個測試包含多個測試用例。輸入的第一行包含一個整數 \(t\)\(1 \leq t \leq 10^4\))—— 測試用例的數量。測試用例說明如下。

每個測試用例的第一行都包含一個整數 \(n\)\(1 \leq n \leq 10^5\))—— 陣列 \(a\) 的長度。

第二行包含 \(n\) 個整數 \(a_1, a_2, \ldots, a_n\)\(2 \leq a_i \leq 4 \cdot 10^5\))—— 陣列 \(a\) 中的元素。可以保證這些元素是成對不同的。

保證所有測試用例中 \(n\) 的總和不超過 \(10^5\)

輸出
對於每個測試用例,輸出一個整數 \(x\)—— 您找到的整數。如果不存在有效的 \(x\),則列印 \(-1\)

如果有多個答案,可以輸出任意一個。

示例
輸入

4
3
8 9 10
4
2 3 4 5
2
147 154
5
3 6 8 25 100000

輸出

2
-1
7
3

提示
在第一個測試用例中,對於 \(x=2\)

\(2\)\(8\) 的生成器,因為我們可以進行以下運算:\(2 \xrightarrow{d=2} 4 \xrightarrow{d=4} 8\)
\(2\)\(9\) 的生成器,因為我們可以進行以下運算:\(2 \xrightarrow{d=2} 4 \xrightarrow{d=2} 6 \xrightarrow{d=3} 9\)
\(2\)\(10\) 的生成器,因為我們可以進行以下運算:\(2 \xrightarrow{d=2} 4 \xrightarrow{d=2} 6 \xrightarrow{d=4} 10\)

在第二個測試用例中,可以證明不可能找到四個整數的公共生成器。

解題思路

由於一個數 \(x\) 可以增加它的因數 \(d\) ,我們很容易就可以想到,要構造一個數 \(a_i\) ,我們只要要構造出它的因數的倍數 \(kd\) 並使得 \(kd \le a_i\),我們就可以很輕鬆的構造出 \(a_i\) 。由於每次操作都是對當前值進行加法,因此我們因數 \(d\) 選的越小,我們的操作空間越大,所以為了構造出 \(a_i\) ,我們一定是選擇它的最小質因數 \(p\) 來進行構造。

  1. 考慮 \(a_i\) 為偶數,我們只要選擇 \(x=2\),就一定可以構造出 \(a_i\)
  2. 考慮 \(a_i\) 為非質數的奇數,我們也只要選擇 \(x=2\),就一定可以構造出 \(a_i\) 。對於\(a_i\),我們只需要將 \(x\) 加上 \(p-1\)\(2\) 就可以獲得因數最小包含 \(a_i\) 最小質數因子 \(p\) 的數 \(2\times p\) 。由於\(a_i\) 為非質數的奇數,其最小質因子最小為 \(3\) ,因此 $a_i \gt 2\times p $,我們只要對 \(2 \times p\) 不斷加上 \(p\) 就可以得到 \(a_i\)
  3. 考慮 \(a_i\) 為質數,我們只能選擇它自己來獲得它。

綜上

  1. 如果 \(a\) 中質數數量大於 \(1\) ,則不存在符合要求的整數。

  2. 如果 \(a\) 中質數數量等於 \(0\) ,則選擇 \(x=2\) 一定可以構造出所有 \(a_i\)

  3. 如果 \(a\) 中質數數量等於 \(1\) ,則我們只能選擇選擇 \(x=\text{pri}\)\(\text{pri}\) 為唯一的質數。

    考慮檢查\(x=\text{pri}\) 是否可以生成所有 \(a_i\)

    • 對於 \(a_i\) 為偶數,只要 \(a_i \le 2\times \text{pri}\) ,我們就可以使得選擇器包含因數 \(2\) 從而構造出所有偶數。
    • 對於 \(a_i\) 為奇數,只要\(a_i - p \le 2\times \text{pri}\) ,我們就可以使得選擇器包含因數 \(2\) 從而構造出 \(2 \times p\) 進而構造出 \(a_i\)

實現程式碼

const int N = 4e5;

vector<int> minp;   // 儲存每個數的最小質因子
vector<int> primes; // 儲存找到的所有質數

// 尤拉篩函式
void sieve(int n)
{
    minp.assign(n + 1, 0); // 初始化最小質因子陣列
    primes.clear();        // 清空質數陣列

    for (int i = 2; i <= n; i++)
    {
        if (minp[i] == 0)
        {                        // 如果 minp[i] 仍為 0,說明 i 是質數
            minp[i] = i;         // 記錄 i 的最小質因子為自身
            primes.push_back(i); // 將 i 新增到質數列表中
        }

        // 遍歷已找到的質數
        for (auto p : primes)
        {
            if (i * p > n)
            { // 如果 i * p 超過 n,停止
                break;
            }
            minp[i * p] = p; // 記錄 i * p 的最小質因子
            if (p == minp[i])
            { // 如果當前質數等於 i 的最小質因子,停止
                break;
            }
        }
    }
}

void solve()
{
    int n;
    cin >> n;
    vi a(n);
    int cntPri = 0;
    int pri = 0;
    for (int i = 0; i < n; i++)
    {
        cin >> a[i];
        if (minp[a[i]] == a[i])
        {
            cntPri++;
            pri = a[i];
        }
    }
    if (cntPri >= 2)
    {
        cout << "-1\n";
        return;
    }
    if (cntPri == 0)
    {
        cout << "2\n";
        return;
    }
    for (int i = 0; i < n; i++)
    {
        if (a[i] == pri)
            continue;
        int v = a[i] & 1 ? a[i] - minp[a[i]] : a[i];
        if (pri * 2 > v)
        {
            cout << "-1\n";
            return;
        }
    }
    cout << pri << "\n";
}

這場沒打,第二天就是區賽,不敢打。

感覺這場挺簡單的。

相關文章