[洛谷][LGR-303-Div.2]洛谷 10 月月赛 I &「WWOI」Round 4 A,B题题解
Q:为什么写这篇题解?
ans:
闲的这次题目都很有意思Q:为什么只有前两题?
ans:
三四不会前两题比较有代表性
AI使用提示
本文少量markdown语法部分使用AI辅助生成,自己手搓太麻烦,但保证所有思路均为自己的
正文
T817800 「WWOI R4」二五一十
1. 题目大意&定义
一个正整数 \(x\) 的有效长度,就是去掉它末尾所有连续的 0 之后,剩余十进制串的长度。
而末尾的 0 来自因子 \(10 = 2 \times 5\)。
因此,去掉末尾的 0,等价于把数中成对的 \(2\) 和 \(5\) 因子全部消去。
二:详细步骤:
1. 分解原数
设原数为 \(n\)。把它分解成:
其中 \(m\) 不能再被 \(2\) 或 \(5\) 整除,即 \(m\) 与 \(10\) 互质。
例如:
- \(182375 = 5^3 \times 1459\),所以 \(m = 1459\)
- \(182376 = 2^3 \times 22797\),所以 \(m = 22797\)
- \(2147483648 = 2^{31}\),所以 \(m = 1\)
2. 操作后的样子
我们可以进行任意次乘 \(2\) 或乘 \(5\) 操作。
假设分别乘了 \(2^x\) 和 \(5^y\),那么结果变为:
去掉末尾所有 0,相当于除以 \(10^{\min(a+x,\ b+y)}\)。
分两种情况:
- 如果 \(a+x \le b+y\),则有效部分为:
- 如果 \(b+y \le a+x\),则有效部分为:
其中 \(d \ge 0\)。
也就是说,无论怎么操作,最终的有效部分一定是:
其中 \(d \ge 0\)。
三. 正确性证明
因为 \(2^d \ge 1\),\(5^d \ge 1\),所以:
乘以一个不小于 \(1\) 的正整数,不会让十进制位数减少。
因此有效部分的位数一定不小于 \(m\) 的位数。
另一方面,我们完全可以不进行任何操作,或者只乘以 \(10\) 的幂(即同时乘 \(2\) 和 \(5\)),这样末尾只会增加 0,去掉后有效部分仍然是 \(m\)。
所以最小有效长度就是 \(m\) 的十进制位数。
5. 算法代码步骤
对于每组数据:
- 读入正整数 \(n\)。
- 不断除以 \(2\),直到 \(n\) 不能再被 \(2\) 整除:
while n % 2 == 0: n /= 2
- 不断除以 \(5\),直到 \(n\) 不能再被 \(5\) 整除:
while n % 5 == 0: n /= 5
- 此时得到的 \(n\) 就是 \(m\)。
- 计算 \(m\) 的十进制位数:
ans = 0
while m > 0:
ans += 1
m /= 10
- 输出 \(ans\)。
6. 复杂度分析(AI计算)
- 每组数据最多除以 \(2\) 约 \(60\) 次,除以 \(5\) 约 \(26\) 次。
- 计算位数最多 \(19\) 次。
- 总时间复杂度约为 \(O(T \log n)\),在 \(T \le 10^6\)、\(n \le 10^{18}\) 下可以接受。
- 需要使用 64 位整数,并建议使用快速输入输出。
7. 样例验证
| 输入 \(n\) | 分解后 \(m\) | \(m\) 的位数 | 输出 |
|---|---|---|---|
| 1 | 1 | 1 | 1 |
| 182375 | 1459 | 4 | 4 |
| 182376 | 22797 | 5 | 5 |
| 2147483648 | 1 | 1 | 1 |
| 1000000000000000000 | 1 | 1 | 1 |
| 999999999999999999 | 999999999999999999 | 18 | 18 |
全部符合样例。
code:
#include<bits/stdc++.h>
using namespace std;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int s;
cin >> s;
while (s--) {
long long n;
cin >> n;
while (n % 2 == 0) n /= 2;
while (n % 5 == 0) n /= 5;
int len = 0;
while (n > 0) {
len++;
n /= 10;
}
cout << len << endl;
}
return 0;
}
题目二
T783160 「WWOI R4」构造大师
这道题我认为非常有意思,很复杂的题目条件,但只需要不到20行代码即可解决,本质数学题。
1. 题目dayi
给定奇数 \(n\) 和 \(1 \le k \le n-1\),需要构造长度为 \(n\) 的数组 \(a,b\),使得
满足 \(|S_1\cup S_2\cup S_3|=3n\)。
因为每个集合最多有 \(n\) 个元素,而总共有 \(3n\) 个模 \(3n\) 的剩余类,所以条件等价于:
- 每个集合内部元素互不相同;
- 三个集合两两不交;
- 并集恰好为 \(\{0,1,\dots,3n-1\}\)。
2. 数学部分
模 \(3n\) 的剩余类可以按照模 \(3\) 的余数分成三组,每组恰好有 \(n\) 个数:
如果我们能
并且每个集合的大小都是 \(n\),那么它们必然分别等于 \(A_0,A_1,A_2\),从而并集就是全体。
所以我们只需要构造 \(a,b\) 使得:
- 所有 \(a_i \equiv 0 \pmod 3\),从而 \(a_i+a_{i+1}\equiv 0\pmod 3\);
- 所有 \(b_i \equiv 1 \pmod 3\),从而 \(a_i+b_i\equiv 1\pmod 3\),且 \(b_i+b_{i+k}\equiv 2\pmod 3\)。
3. 构造方式
即
这些数都在 \(0\) 到 \(3n-1\) 之间。
4. 正确性证明(部分AI写markdown)
(1) 模 \(3\) 性质
- \(a_i \equiv 0 \pmod 3\),所以 \(a_i+a_{i+1}\equiv 0\pmod 3\),即 \(S_1\subseteq A_0\)。
- \(b_i \equiv 1 \pmod 3\),所以 \(a_i+b_i\equiv 1\pmod 3\),即 \(S_2\subseteq A_1\)。
- \(b_i+b_{i+k}\equiv 1+1\equiv 2\pmod 3\),即 \(S_3\subseteq A_2\)。
三个集合分别放在三个不同的模 \(3\) 数字类,肯定互不相交。
(2) 每个集合恰好取遍对应模 \(3\) 类的所有数
只需证明每个集合内部元素互不相同,且各有 \(n\) 个。
对于 \(S_1\):
模 \(3n\) 下,这些值等于 \(3 \cdot ((2i-1)\bmod n)\)(最后一个为 \(3\cdot((n-1)\bmod n)\))。
因为 \(n\) 是奇数,\(\gcd(2,n)=1\),所以 \(i\mapsto 2i-1\pmod n\) 是 \(1,2,\dots,n-1\) 的一个排列,再加上 \(i=n\) 对应的 \(n-1\),正好取遍 \(0,1,\dots,n-1\)。
因此 \(S_1 = \{3k \bmod 3n \mid 0\le k\le n-1\} = A_0\)。
对于 \(S_2\):
模 \(3n\) 下,这等于 \(1+3\cdot((2(i-1))\bmod n)\)。
因为 \(2\) 与 \(n\) 互质,\(2(i-1)\bmod n\) 取遍 \(0,1,\dots,n-1\)。
所以 \(S_2 = \{1+3k \mid 0\le k\le n-1\} = A_1\)。
对于 \(S_3\):
模 \(3n\) 下,这等于 \(2+3\cdot((2i+k-2)\bmod n)\)。
当 \(i=1,\dots,n\) 时,\(2i+k-2\pmod n\) 是一个线性函数,因为 \(2\) 与 \(n\) 互质,所以它取遍所有模 \(n\) 的剩余类。
因此 \(S_3 = \{2+3k \mid 0\le k\le n-1\} = A_2\)。
于是
并集大小为 \(3n\),满足要求。
5. 复杂度分析
构造只需对 \(i=1,\dots,n\) 直接计算 \(a_i,b_i\),时间复杂度 \(O(n)\),空间复杂度 \(O(n)\)。
对于所有测试数据,总 \(n\) 不超过 \(10^6\),可以轻松通过。
6. 样例验证
以 \(n=5,k=2\) 为例,按照本构造:
计算模 \(15\):
- \(S_1\):
得 \(\{0,3,6,9,12\}\)。
- \(S_2\):
得 \(\{1,4,7,10,13\}\)。
- \(S_3\):
得 \(\{2,5,8,11,14\}\)。
三个集合的并集正好是 \(\{0,1,2,\dots,14\}\),满足条件。
7. 结论
对于任意奇数 \(n\) 和任意 \(1\le k\le n-1\),答案总是 Yes。
一种简单且通用的构造是:
直接输出即可。
code:
#include <bits/stdc++.h>
#define int long long
using namespace std;
signed main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n, k;
cin >> n >> k;
cout << "Yes" << '\n';
for (int i = 1; i <= n; ++i) {
cout << 3 * i - 3 << (i == n ? '\n' : ' ');
}
for (int i = 1; i <= n; ++i) {
cout << 3 * i - 2 << (i == n ? '\n' : ' ');
}
return 0;
}
后记
第一题其实我第一遍交没过,原因是 TLE,但是加入
ios::sync_with_stdio(false);
cin.tie(nullptr);
就过了,这显然打脸了***说过的“我至今没有见过任何题加入优化输入输出流后变 AC”,嘻嘻。
其次这篇题解我真的写的很认真,找数学符号对应的 Markdown 真的很麻烦,整篇写下来花了很长时间,点个赞吧 QAQ
浙公网安备 33010602011771号