CodeTON Round 9 div 1+2 个人题解(A~E)

CodeTON Round 9 div 1+2 个人题解(A~E)

Dashboard - CodeTON Round 9 (Div. 1 + Div. 2, Rated, Prizes!) - 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. Shohag Loves Mod

Shohag 有一个整数 \(n\)。请帮他找出一个递增的整数序列 \(1 \leq a_1 < a_2 < \ldots < a_n \leq 100\),使得 \(a_i \mod i \neq a_j \mod j\) 在所有 \(1 \leq i < j \leq n\) 的对中都成立。

可以证明,在给定的约束下,总是存在这样的序列。

输入
第一行包含一个整数 \(t\) (\(1 \leq t \leq 50\)) — 测试用例数。
每个测试用例的第一行,也是唯一一行,包含一个整数 \(n\) (\(2 \leq n \leq 50\))。

输出
对于每个测试用例,打印 \(n\) 个整数——满足语句中所述条件的整数序列。如果有多个这样的序列,则输出任意一个。

示例
输入

2
3
6

输出

2 7 8
2 3 32 35 69 95

注意
在第一个测试用例中,序列是递增的,值在 \(1\)\(100\) 之间,并且每对索引满足语句中提到的条件:

对于对 \((1, 2)\)\(a_1 \mod 1 = 2 \mod 1 = 0\)\(a_2 \mod 2 = 7 \mod 2 = 1\)。所以它们是不同的。
对于对 \((1, 3)\)\(a_1 \mod 1 = 2 \mod 1 = 0\)\(a_3 \mod 3 = 8 \mod 3 = 2\)。所以它们是不同的。
对于对 \((2, 3)\)\(a_2 \mod 2 = 7 \mod 2 = 1\)\(a_3 \mod 3 = 8 \mod 3 = 2\)。所以它们是不同的。
请注意,您不必打印完全相同的序列,只要打印满足必要条件的任何其他序列即可。

解题思路

我们贪心的想,如果要让我们在后面限制尽可能的少,我们应该构建出 \(a_i \mod i = 0,1,2,3,\dots\) 这样的序列。

实际上就是 \(1,3,5,7, \dots , 2i-1\)

实现代码

void solve()
{
    int n;
    cin >> n;
    for (int i = 1; i <= n; i++)
    {
        cout << 2 * i - 1 << " \n"[i == n];
    }
}

B. Shohag Loves Strings

对于字符串 \(p\),设 \(f(p)\)\(p\) 的唯一非空子串的个数。

Shohag 有一个字符串 \(s\)。请帮助他找到一个非空字符串 \(p\),使得 \(p\)\(s\) 的子串,且 \(f(p)\) 是偶数,或者指出不存在这样的字符串。

输入
第一行包含一个整数 \(t\) (\(1 \leq t \leq 10^4\)) - 测试用例数。
每个测试用例的第一行,也是唯一一行,包含一个由小写英文字母组成的字符串 \(s\) (\(1 \leq |s| \leq 10^5\))。
保证所有测试用例中 \(s\) 的长度总和不超过 \(3 \cdot 10^5\)

输出
对于每个测试用例,打印一个满足语句中所述条件的非空字符串,如果不存在这样的字符串,则打印 \(-1\)。如果有多个解决方案,则输出任意一个。

示例
输入

5
dcabaac
a
youknowwho
codeforces
bangladesh

输出

abaa
-1
youknowwho
eforce
bang

注意
在第一个测试用例中,我们可以设 \(p = \text{abaa}\),因为它是 \(s\) 的子串,且 \(p\) 的唯一非空子串有 \(a, b, aa, ab, ba, aba, baa\)\(abaa\),因此总共有 \(8\) 个不同的子串,这是偶数。

在第二个测试用例中,我们只能设 \(p = a\),但它只有一个唯一非空子串,因此这个数字是奇数,不有效。

在第三个测试用例中,整个字符串包含 \(52\) 个不同的非空子串,因此字符串本身是一个有效的解决方案。

解题思路

观察发现

  1. 对于长度为 \(1\) 的子串:显然不符合条件。

  2. 对于长度为 \(2\) 的子串:

    • 如果两个字母相同,即形如 \(aa\) ,所有子串为: \(a,aa\) 。为偶数,符合条件。
    • 如果两个字母不同,即形如 \(ab\) ,所有子串为: \(a,b,ab\) 。为奇数,不符合条件。
  3. 对于长度为 \(3\) 的子串:

    • 如果存在连续两个字母相同,即形如 \(aab\)\(abb\) ,包含在情况 \(1\) 中。

    • 如果有两个字母相同但不连续,即形如 \(aba\) ,所有子串为: \(a,b,ab,ba,aba\) 。为奇数,不符合条件。

    • 如果三个字母都不同,即形如 \(abc\) ,所有子串为: \(a,b,c,ab,bc,abc\) 。为偶数,符合条件。

  4. 对于长度为 \(4\) 及以上的子串:被前三种情况涵盖

所以只需要检查字符串中是否存在形如 \(aa\)\(abc\) 的子串即可。

实现代码

void solve()
{
    string s;
    cin >> s;
    int n = s.size();
    if (n == 1)
    {
        cout << "-1\n";
        return;
    }

    for (int i = 1; i < n; i++)
    {
        if (s[i] == s[i - 1])
        {
            cout << s[i - 1] << s[i] << "\n";
            return;
        }
    }
    for (int i = 2; i < n; i++)
    {
        if (s[i] != s[i - 1] && s[i] != s[i - 2] && s[i - 1] != s[i - 2])
        {
            cout << s[i - 2] << s[i - 1] << s[i] << "\n";
            return;
        }
    }
    cout << "-1\n";
}

C1. Shohag Loves XOR (Easy Version)

这是问题的简单版本。两个版本的不同之处以粗体标出。只有两个版本的问题都解决了,才能进行破解。

Shohag 有两个整数 \(x\)\(m\)。帮他数出有多少个整数 \(1 \leq y \leq m\)\(x \neq y\)\(x \oplus y\)整数除数,这些整数要么是 \(x\),要么是 \(y\),要么两者都是。这里的 \(\oplus\) 是按位异或操作符。

输入
第一行包含一个整数 \(t\) (\(1 \leq t \leq 10^4\)) - 测试用例数。
每个测试用例的第一行,也是唯一一行,包含两个空格分隔的整数 \(x\)\(m\) (\(1 \leq x \leq 10^6\), \(1 \leq m \leq 10^{18}\))。
保证所有测试用例中 \(x\) 的总和不超过 \(10^7\)

输出
为每个测试用例打印一个整数,即合适的 \(y\) 个数。

示例
输入

5
6 9
5 7
2 3
6 4
4 1

输出

3
2
1
1
0

注意
在第一个测试用例中,对于 \(x=6\),在 \(1\)\(m=9\) 的整数中,有 \(3\) 个有效的 \(y\),它们是 \(4\)\(5\)\(7\)

\(y=4\) 是有效的,因为 \(x \oplus y = 6 \oplus 4 = 2\),而 \(2\)\(x=6\)\(y=4\) 的除数。
\(y=5\) 是有效的,因为 \(x \oplus y = 6 \oplus 5 = 3\),而 \(3\)\(x=6\) 的除数。
\(y=7\) 是有效的,因为 \(x \oplus y = 6 \oplus 7 = 1\),而 \(1\)\(x=6\)\(y=7\) 的除数。
在第二个测试用例中,对于 \(x=5\),在 \(1\)\(m=7\) 的整数中,有 \(2\) 个有效的 \(y\),它们是 \(4\)\(6\)

\(y=4\) 是有效的,因为 \(x \oplus y = 5 \oplus 4 = 1\),而 \(1\)\(x=5\)\(y=4\) 的除数。
\(y=6\) 是有效的,因为 \(x \oplus y = 5 \oplus 6 = 3\),而 \(3\)\(y=6\) 的除数。

解题思路

我们可以预处理出所有 \(x\) 的因数,然后枚举这些因数 \(d\) ,计算出 $y=x \oplus d $ ,判断是否满足 \(y\le m \and y\neq x\)

这样我们就获得了所有符合条件的 \(x\) 的因数数量 \(cnt1\)

然后我们再枚举所有所有 \(y\le x\) 的数,统计出所有符合条件的 \(y\) 并去之前枚举过程中预计的符合条件的 \(y\) 做一下容斥即可。

实现代码

const int N = 1e6 + 5;
vi divi[N];

void getDivi()
{
    for (int i = 1; i < N; i++)
    {
        for (int j = i; j < N; j += i)
        {
            divi[j].pb(i);
        }
    }
}
void solve()
{
    ll x, m;
    cin >> x >> m;
    ll cnt1 = 0, cnt2 = 0, cnt12 = 0;
    for (auto &d : divi[x])
    {
        ll y = x ^ d;
        if (y >= 1 && y <= m && y != x)
        {
            cnt1++;
            if (y % d == 0)
                cnt12++;
        }
    }
    for (int d = 1; d <= x; d++)
    {
        ll y = x ^ d;
        if (y >= 1 && y <= m && y != x)
        {
            if (y % d == 0)
                cnt2++;
        }
    }
    ll ans = cnt1 + cnt2 - cnt12;
    cout << ans << "\n";
}

C2. Shohag Loves XOR (Hard Version)

这是问题的困难版本。两个版本之间的差异以粗体标出。只有两个版本的问题都解决了,才能进行破解。

Shohag 有两个整数 \(x\)\(m\) 。帮他数出 \(1 \leq y \leq m\) 中,\(x \oplus y\) 能被 \(x\)\(y\) 这两个数整除的整数的个数。这里的 \(\oplus\) 是按位异或运算符。

输入
第一行包含一个整数 \(t\) (\(1 \leq t \leq 10^4\)) - 测试用例数。
每个测试用例的第一行,也是唯一一行,包含两个空格分隔的整数 \(x\)\(m\) (\(1 \leq x \leq 10^6\), \(1 \leq m \leq 10^{18}\))。
保证所有测试用例中 \(x\) 的总和不超过 \(10^7\)

输出
为每个测试用例打印一个整数,即合适的 \(y\) 个数。

示例
输入

5
7 10
2 3
6 4
1 6
4 1

输出

3
2
2
6
1

注意
在第一个测试用例中,对于 \(x=7\),在 \(1\)\(m=10\) 的整数中,有 \(3\) 个有效的 \(y\),它们是 \(1\)\(7\)\(9\)

\(y=1\) 是有效的,因为 \(x \oplus y = 7 \oplus 1 = 6\),而 \(6\) 能被 \(y=1\) 整除。
\(y=7\) 是有效的,因为 \(x \oplus y = 7 \oplus 7 = 0\),而 \(0\) 能被 \(x=7\)\(y=7\) 整除。
\(y=9\) 是有效的,因为 \(x \oplus y = 7 \oplus 9 = 14\),而 \(14\) 能被 \(x=7\) 整除。

解题思路

分析 \(x \oplus y\) 是否能被 \(x\)\(y\) 或两者同时整除。

  1. \(x \oplus y\) 能被 \(x\) 整除

    \(z = x \oplus y\),则 \(y = z \oplus x\) 。因为 \(z \oplus x \leq z + x\),在 \(z \leq m\) 时,几乎所有 \(z\) 都可行,所以数量为 \(\lfloor \frac{m - x}{x} \rfloor\)

    对于 \(p > m - x\),只需检查区间 \((m - x, m + x]\) 中的两个 \(x\) 的倍数。

  2. \(x \oplus y\) 能被 \(y\) 整除

    \(x < y\) 时,\(x \oplus y < 2y\),没有解。

    \(x \geq y\),遍历所有 \(y \leq x\) 的值,统计 \(x \oplus y\) 能被 \(y\) 整除的数量即可。

  3. \(x \oplus y\) 能被 \(x\)\(y\) 同时整除

    这意味着 \(x \oplus y\) 能被 \(\text{lcm}(x, y)\) 整除。当 \(x \neq y\) 时,\(\text{lcm}(x, y) \geq 2 \cdot \max(x, y)\),而 \(x \oplus y < 2 \cdot \max(x, y)\),所以没有解,只有在 \(y = x\) 时才会出现这种情况。

综上,答案为情况一的结果加上情况二的结果,再减去情况三的结果。

实现代码

void solve()
{
    ll x, m;
    cin >> x >> m;
    ll z = m - m % x;
    ll ans = z / x;
    if (x < z)
        ans--;
    if ((x ^ z) >= 1 && (x ^ z) <= m)
        ans++;
    if ((x ^ (z + x)) >= 1 && (x ^ (z + x)) <= m)
        ans++;
    if (x <= m)
        ans--;
    for (ll y = 1; y <= min(x, m); y++)
    {
        ans += ((x ^ y) % y == 0);
    }
    cout << ans << '\n';
}

D. Shohag Loves GCD

舒哈格有一个整数 \(n\) 和由 \(m\) 个唯一整数组成的集合 \(S\)。请帮他找出 \(\text{最大的整数数组}^*\) \(a_1, a_2, \ldots, a_n\),使得每个 \(1 \leq i \leq n\)\(a_i \in S\)\(\gcd(i,j) \neq \gcd(a_i,a_j)\) 在所有 \(1 \leq i < j \leq n\) 对中都满足,或者指出不存在这样的数组。

如果 a≠b,并且在 a 和 b 不同的第一个位置上,数组 a 的元素比 b 中的相应元素大,那么数组 a 在词法上比相同长度的数组 b 大。

输入
第一行包含一个整数 \(t\) (\(1 \leq t \leq 10^4\)) - 测试用例数。

每个测试用例的第一行包含两个整数 \(n\)\(m\) (\(1 \leq m \leq n \leq 10^5\))。

第二行包含 \(m\) 个唯一整数,按递增顺序排列,代表集合 \(S\) 的元素 (\(1 \leq x \leq n\) 对于每个 \(x \in S\))。

保证所有测试用例中 \(n\) 的总和不超过 \(3 \cdot 10^5\)

输出
对于每个测试用例,如果没有解决方案,则打印 \(-1\),否则打印 \(n\) 个整数——满足条件的词典上最大的整数数组。

示例
输入

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

输出

6 4 4 3 4 3
1
-1

注意
在第一个测试用例中,数组中的每个元素都属于给定的集合 \(S=\{3,4,6\}\),并且数组的所有索引对满足必要条件。特别地,对于对 \((2,3)\)\(\gcd(2,3)=a_1=6\)\(\gcd(a_2,a_3)=\gcd(4,4)=4\),因此它们不相等。还有其他满足条件的数组,但这个数组在词典上是最大的。

在第三个测试用例中,不可能有解决方案,因为我们只能使用 \(a=[2,2]\),但对于这个数组,对于对 \((1,2)\)\(\gcd(1,2)=a_1=2\)\(\gcd(a_1,a_2)=\gcd(2,2)=2\),因此它们相等,这是不允许的!

解题思路

观察发现,对于 \(i = 1\) ,由于 \(\gcd(k, 1) = 1\) , 对于任意整数 \(k\) ,我们可以得出结论:

若在位置 \(i=1\) 位置填入最大值 \(s_m\) ,则在其它位置 \(j\)\(j|1,j\neq i\) )的可填最大值为 \(s_{m-1}\)

推广可得:对于任意位置 \(i\),设其可填最大值 \(a_i=s_k\)\(\forall j|i\and j \neq i\) , \(a_j\lt a_i\)

因此对所有 \(i\) 做一个筛法即可。

实现代码

void solve()
{
    int n, m;
    cin >> n >> m;
    vi s(m);
    for (int i = 0; i < m; i++)
    {
        cin >> s[i];
    }
    vi a(n + 1, inf);
    a[1] = m - 1;
    for (int i = 1; i <= n; i++)
    {
        for (int j = i + i; j <= n; j += i)
        {
            a[j] = min(a[i] - 1, a[j]);
        }
    }
    for (int i = 1; i <= n; i++)
    {
        if (a[i] < 0)
        {
            cout << "-1\n";
            return;
        }
    }
    for (int i = 1; i <= n; i++)
    {
        cout << s[a[i]] << " \n"[i == n];
    }
}

E. Shohag Loves Inversions

舒哈格有一个整数数组 \(a\)。最初为 \(a=[0,1]\)。他可以重复执行以下操作任意多次:

假设 \(k\) 是当前数组 \(a\) 中的倒数*的个数。将 \(k\) 插入 \(a\) 中的任意位置,包括开头或结尾。例如,如果 \(a=[4,6,2,4]\),那么反转数就是 \(k=3\)。因此,肖哈格可以在运算后得到以下数组:\([3,4,6,2,4]\)\([4,3,6,2,4]\)\([4,6,3,2,4]\)\([4,6,2,3,4]\)\([4,6,2,4,3]\)

在给定整数 \(n\) 的情况下,帮助肖哈格计算在进行运算后可以得到的长度为 \(n\) 的不同数组的个数,取模 \(998244353\)

  • 数组 \(a\) 中的反转数是指 \(i < j\)\(a_i > a_j\) 这样的一对索引 \((i, j)\) 的个数。

输入
第一行包含一个整数 \(t\) (\(1 \leq t \leq 10^4\)) - 测试用例数。

每个测试用例的第一行,也是唯一一行,包含一个整数 \(n\) (\(2 \leq n \leq 10^6\))。

保证所有测试用例中 \(n\) 的总和不超过 \(10^6\)

输出
针对每个测试用例,输出一个整数——可能的数组数量取模 \(998244353\)

示例
输入

4
4
2
7
69

输出

5
1
682
325188814

注意
在第一个测试用例中,可以获得以下 \(5\) 个数组(插入的倒数计数以粗体显示):

\([0,1] \rightarrow [0,0,1] \rightarrow [0,0,1,0]\)
\([0,1] \rightarrow [0,0,1] \rightarrow [0,0,0,1]\)
\([0,1] \rightarrow [0,1,0] \rightarrow [0,1,0,1]\)
\([0,1] \rightarrow [0,1,0] \rightarrow [0,1,1,0]\)
\([0,1] \rightarrow [0,1,0] \rightarrow [1,0,1,0]\)

解题思路

关键发现,由于初始数组的元素仅为 0 和 1,逆序对数量超过 1 时,当我们插入的值将大于数组中的任何元素。因此,我们可以通过这种方式来控制逆序对数量。

我们把逆序对数量大于 \(\max(a)\) 的数组称为好数组,其它称为坏数组,假设当前正在进行转移,如果我们从好数组转移到坏数组,将带来很多复杂情况,容易导致重复计数。因此,我们应该设计出好数组到好数组的转移方程。

\(dp[i]\) 为当前数组长度为 \(i\) 时,得到长度为 \(n\) 的最终数组的数量,前提是当前数组为好数组。

设当前数组中的逆序对数量为 \(k\),且 \(k > \max(a)\)。那么:

  • 如果我们在末尾插入 \(k\),那么新的逆序对数量仍将为 \(k\)。因此,如果我们在末尾插入 \(j\)\(k\),并且在其他地方插入一次(有 \(i\) 种方式),那么我们将得到 \(dp_{i+j+1}\) 的情况。
  • 如果我们在末尾以外的地方插入 \(k\),那么新的逆序对数量将超过 \(k\),这将使我们得到 \(dp[i+1]\) 的相同情况。

因此,\(dp[i] = (i \cdot \sum_{j > i} dp[j]) + 1\),这里的 \(1\) 是因为我们可以通过在末尾插入\(n - i\)\(k\)

现在我们只需处理初始数组转变为好数组的情况数量。

显然可以构造出 \(n - 1\) 种逆序对数量小于等于 1 的序列。它们的形式为 \(0, 0, \ldots, 0, [0, 1, 0], 1, \ldots, 1, 1\)

考虑得到长度为 \(m\) 的好数组的方式,我们可以发现,只需要要在上面的序列中第一个 1 之前插入 1 即可获得一个好数组。

若第一个 1 的位置为 \(j\),则我们有 \(j - 1\) 种方式。

因此,得到长度为 \(m\) 的好数组的方案数为:

\[\sum_{j=2}^{m - 1} (j - 1) = \frac{(m - 2) \cdot (m - 1)}{2} - 1 \]

因此,答案就是:

\[n - 1 + \sum_{m=3}^{n} \left( \frac{(m - 2) \cdot (m - 1)}{2} - 1 \right) \cdot dp[m] \]

实现代码

const ll mod = 998244353;
void solve()
{
    ll n;
    cin >> n;
    ll sum = 0;
    vl dp(n + 1);
    for (ll i = n; i >= 1; i--)
    {
        dp[i] = (i * sum % mod + 1) % mod;
        sum = (sum + dp[i]) % mod;
    }
    ll ans = n - 1;
    for (ll k = 3; k <= n; k++)
    {
        ll t = ((k - 1) * (k - 2) / 2 - 1 + mod) % mod;
        ans = (ans + t * dp[k] % mod) % mod;
    }
    cout << ans << '\n';
}

数学场,一堆因数相关的题目。

c2折腾半天没写完,赛后才把情况讨论完。

封面画师id:竜崎いち

posted @ 2024-11-24 06:31  ExtractStars  阅读(80)  评论(0编辑  收藏  举报