CodeTON Round 9 题解(A~E)

A. Shohag Loves Mod

直接构造 \(2i-1\) 即可,因为 \(2i-1\ mod\ i = i - 1\)

#include <bits/stdc++.h>

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

int main()
{
	std::ios::sync_with_stdio(false);
	std::cin.tie(nullptr);

	int t; std::cin >> t;
	while(t--) solve();

	return 0;
}

B. Shohag Loves Strings

观察一下发现连续两个相同的字符或者连续三个不同的字符是合法的,直接枚举判断

#include <bits/stdc++.h>

void solve()
{
	std::string s; std::cin >> s;
	int n = s.size();

	for(int i = 0; i + 1 < n; i++)
	{
		if(s[i] == s[i + 1])
		{
			std::cout << s[i] << s[i] << "\n";
			return;
		}
	}

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

	std::cout << -1 << "\n";
}

int main()
{
	std::ios::sync_with_stdio(false);
	std::cin.tie(nullptr);

	int t; std::cin >> t;
	while(t--) solve();

	return 0;
}

C1. Shohag Loves XOR (Easy Version)

要满足 \((x\oplus y)\ |\ x\) 或者 \((x \oplus y)\ |\ y\),首先可以观察到当 \(y\) 的二进制位比 \(x\) 多的时候是一定不合法的,因为这个时候 \(x \oplus y > x\) 并且 \(x \oplus y < 2y\),所以只需要枚举到跟 \(x\) 位数相同的数判断就好

#include <bits/stdc++.h>

using i64 = long long;

void solve()
{
	int x; std::cin >> x;
	i64 m; std::cin >> m;

	int log = std::__lg(x) + 1;
	int N = 1 << log;
	int ans = 0;
	for(int i = 1; i < N && i <= m; i++)
	{
		int t = x ^ i;
		if(t == 0) continue;
		if(x % t == 0 || i % t == 0) ans++;
	}

	std::cout << ans << "\n";
}

int main()
{
	std::ios::sync_with_stdio(false);
	std::cin.tie(nullptr);

	int t; std::cin >> t;
	while(t--) solve();

	return 0;
}

后来发现好像有个结论 \(x - y < x \oplus y < x + y\),知道这个就更简单了

C2. Shohag Loves XOR (Hard Version)

小范围跟上题做法一样,同时我们也能观察到大范围的时候 \(y\ |\ (x \oplus y)\) 是不可能的,原因见上,所以只可能是 \(x\ |\ (x \oplus y)\),这一部分就是 \(m\) 内所有 \(x\) 的倍数,也就是 \(\frac{m}{x}\),但是这些数并不是 \(y\),而是 \(x \oplus y\),所以在边界处需要再处理一下

#include <bits/stdc++.h>

using i64 = long long;

void solve()
{
	int x; std::cin >> x;
	i64 m; std::cin >> m;

	int log = std::__lg(x) + 1;
	int N = 1 << log;
	i64 ans = 0;
	for(int i = 1; i < N && i <= m; i++)
	{
		int t = x ^ i;
		if(t % x == 0 || t % i == 0) ans++;
	}

	i64 max = m / x;
	if(m >= N - 1)
	{
		ans += max;
		for(int i = 1; 1LL * i * x < N; i++) // 已经在上面算过了
		{
			i64 t = i * x ^ x;
			ans--;
		}

		for(i64 i = max, j = 100; i * x >= N && j; j--, i--) // 删掉一些不合法的
		{
			i64 t = i * x ^ x;
			if(t > m) ans--;
		}
		for(i64 i = max + 1, j = 0; i * x >= N && j < 100; j++, i++) // 加上一些可能漏判的
		{
			i64 t = i * x ^ x;
			if(t <= m) ans++;
		}
	}

	std::cout << ans << "\n";
}

int main()
{
	std::ios::sync_with_stdio(false);
	std::cin.tie(nullptr);

	int t; std::cin >> t;
	while(t--) solve();

	return 0;
}

D. Shohag Loves GCD

要求字典序最大,所以我们优先填大的数,同时如果 \(a_i\) 确定了,那么 \(a_i \neq a_{ki}\),同时也能得到 \(a_i > a_{ki}\),因为我们一定是从大往小填数,这样的话就不会存在 \(a_i = gcd(a_{xi}, a_{yi})\) 的情况。判断答案是否合法只需要判断每个位置是不是都有数可以填即可。

#include <bits/stdc++.h>

using i64 = long long;

void solve()
{
	int n, m; std::cin >> n >> m;
	std::vector<int> s(m);
	for(int i = 0; i < m; i++) std::cin >> s[i];
	std::ranges::sort(s, std::greater());

	std::vector<int> a(n);
	for(int i = 1; i <= n; i++)
		for(int j = 2 * i; j <= n; j += i)
			if(a[j - 1] == a[i - 1])
				a[j - 1]++;

	if(*max_element(a.begin(), a.end()) >= m) 
	{
		std::cout << -1 << "\n";
		return;
	}

	for(int i = 0; i < n; i++) std::cout << s[a[i]] << " \n"[i == n - 1];
}

int main()
{
	std::ios::sync_with_stdio(false);
	std::cin.tie(nullptr);

	int t; std::cin >> t;
	while(t--) solve();

	return 0;
}

E. Shohag Loves Inversions

首先我们可以注意到,逆序对的数量是单调不减的,这就意味着,一个数组只有一种产生方式,因为大的数一定是在小的数出现之后再出现的(这里我们把连续相同的数字的产生方式全部定义为加入到这些数的末尾),所以我们在计数的过程中不需要考虑重复的问题。

同时我们可以发现,由于初始的数组只包含 \(0\)\(1\),所以当我们可以把大于 \(1\) 的数加入时,只要不放在末尾,逆序数一定会增大,并且放在不同的位置产生的新的逆序数也不一样。我们不妨设 \(f_i\) 表示大小为 \(i\) 的逆序数大于 \(1\) 数组的数量,那么有

\[f_i=\sum_{j=1}^{i-1}\ j * f_j \]

这是因为,对于一个长度为 \(j\) 的数组,有 \(j\) 个位置可以增加逆序数,而末尾的位置可以保持逆序数,所以任意长度的数组都可以不断往末尾加数到 \(i - 1\) 大小,再往 \(j\) 个位置中的其中一个加数。

现在让我们考虑初始情况,可以发现形如 \(0,...,0,0,1,0,1,...,1\) 的数组只有 \(1\) 的逆序数,但是将这个 \(1\) 插入到第一个 \(1\) 之前的所有位置都会增加逆序数,即第一个 \(1\) 如果位于第 \(i\) 位,就有 \(i\) 种方式,而这种数组可以由 \(0,1\) 先不断在 \(1\) 前加 \(0\),再往 \(1\) 后加一个 \(0\),再不断往末尾加 \(1\) 得到,所以长度为 \(i\) 的数组一共会有 \(i - 2\) 种这样的形式,而第一个 \(1\) 的位置的范围为 \([2,\ i - 2]\),故

\[f_i = \sum_{j=2}^{i-2}\ j = \frac{(i-1)(i-2)}{2}-1 \]

最后的答案为

\[n - 1 + \sum_{i=2}^{n}\ f_i \]

因为每个长度的数组都可以一直往后加数,同时加上逆序数小于等于 \(1\) 的方案数。

#include <bits/stdc++.h>

using i64 = long long;

constexpr int P = 998244353;
constexpr int N = 1e6;

i64 f[N + 1];

void solve()
{
	int n; std::cin >> n;
	std::cout << (f[n] + n - 1) % P << "\n";
}

int main()
{
	std::ios::sync_with_stdio(false);
	std::cin.tie(nullptr);

	for(int i = 3; i <= N; i++) f[i] = (1LL * (i - 2) * (i - 1) / 2 - 1) % P;
	i64 sum = 0;
	for(int i = 1; i <= N; i++)
	{
		f[i] = (f[i] + sum) % P;
		sum = (sum + i * f[i] % P) % P;
		f[i] = (f[i] + f[i - 1]) % P;
	}

	int t; std::cin >> t;
	while(t--) solve();

	return 0;
}
posted @ 2024-11-24 21:45  Repeater11  阅读(59)  评论(0编辑  收藏  举报