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\) 的子串:显然不符合条件。
-
对于长度为 \(2\) 的子串:
- 如果两个字母相同,即形如 \(aa\) ,所有子串为: \(a,aa\) 。为偶数,符合条件。
- 如果两个字母不同,即形如 \(ab\) ,所有子串为: \(a,b,ab\) 。为奇数,不符合条件。
-
对于长度为 \(3\) 的子串:
-
如果存在连续两个字母相同,即形如 \(aab\) 或 \(abb\) ,包含在情况 \(1\) 中。
-
如果有两个字母相同但不连续,即形如 \(aba\) ,所有子串为: \(a,b,ab,ba,aba\) 。为奇数,不符合条件。
-
如果三个字母都不同,即形如 \(abc\) ,所有子串为: \(a,b,c,ab,bc,abc\) 。为偶数,符合条件。
-
-
对于长度为 \(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\) 或两者同时整除。
-
\(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\) 的倍数。
-
\(x \oplus y\) 能被 \(y\) 整除
当 \(x < y\) 时,\(x \oplus y < 2y\),没有解。
当 \(x \geq y\),遍历所有 \(y \leq x\) 的值,统计 \(x \oplus y\) 能被 \(y\) 整除的数量即可。
-
\(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\) 的好数组的方案数为:
因此,答案就是:
实现代码
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:竜崎いち