Codeforces Round 985 div1+2 个人题解(A~E)

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";
}

这场没打,第二天就是区赛,不敢打。

感觉这场挺简单的。

posted @ 2024-11-13 11:15  ExtractStars  阅读(14)  评论(0编辑  收藏  举报