「GOI-R1」 合成台 官方题解
合成台 官方题解
题目描述
一次转圣操作定义为:向合成台中投入
你拥有
你需要求出在整个过程最后(即剩余物品数量不足
数据范围
对于
题解
Subtask 1
20 暴力分。理论上复杂度为
Subtask 2
首先,当
当
显然
通过反证法,不难证明最后得到的物品一定为
所以当
设
但我们不可能读入 100 位的整数;我们只需要读入字符串,将最后一位对
Subtask 3 & Solution
与 Subtask 2 类似:
当
经过一次转圣操作后,就会得到
由 Subtask 2 的结论,同奇偶等价于与
同样有:最后得到的物品数一定
设
由于在
上式对于
但我们不太可能对一个 100 位的整数减一,很繁琐;因此我们可以对其进行一些变形:
为使除数非负以不影响取模,加上一个
到这里问题转化为求
以整数
我们可以将其分解为
由于取模运算具有同加性与同乘性,我们可以计算出每一位对于答案的贡献再相加取模。
以第一位为例,计算
我们分别计算每一位的贡献,在计算答案的时候边相加边取模,最终就可以得到
设这个整数的位数为
注意:
下面是代码:
#include <bits/stdc++.h>
#define ll long long
using namespace std;
int mod, mul10[107], len;
//预处理出 (10 ^ x) mod (s - 1) 的值
void initmul10()
{
mul10[0] = 1 % mod; //s - 1 可能为 1
for (int i = 1; i <= len; i++)
mul10[i] = (mul10[i - 1] * 10) % mod;
}
//计算每一位对答案的贡献 (x * (10 ^ y)) mod (s - 1)
int calcxmul10y(int x, int y)
{
return (x * mul10[y]) % mod;
}
//计算答案
int nmod(string n)
{
int ans = 0;
for (int i = 0; i < len; i++)
{
//字符串第 i 位为整数的第 len - i 位
//例如:
// 114
// i 012
// len-i 321
int num = len - i, x = n[i] - '0';
ans = (ans + calcxmul10y(x, num - 1)) % mod;
}
return ans;
}
int main()
{
int t;
cin >> t;
while (t--)
{
int s;
string n;
cin >> s >> n;
if (n == "0")
{
cout << "0\n";
continue;
}
mod = s - 1;
len = n.size();
initmul10();
int ans = 1 + (nmod(n) + s - 2) % mod;
cout << ans << '\n';
}
return 0;
}
为什么不是 AC 代码呢?因为它 T 了...
Subtask 4
本题在题面中给出了常数预警,而想到常数优化,第一个想到的自然是——
快读!
然而对于这样大的一个整数,无论怎样读取都是无法存储的。
稍加思考,不难发现在本题中我们需要知道
-
n$ 是否为 $0 -
前者自然可以用快读处理;
而对于后者,思考快读的核心操作:
char ch = getchar();
x *= 10;
x += ch - '0';
其中只包含了加法与乘法运算。
因此,我们可以一边快读一边进行取模操作,这样自然就知道了
将其与 Subtask 3 的做法对比,Subtask 3 的做法需要:
-
读入字符串,
O(Tl) -
预处理
10^i \bmod (s-1) ,O(Tl) -
计算
n \bmod (s-1) ,O(Tl)
而本做法只需要第一项的
下面是 AC 代码:
#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <cstring>
using namespace std;
bool is_zero = false; //判断是否是0
inline int read(int mod)
{ //快读,边读入边取模
int x = 0;
char ch = getchar();
while (!isdigit(ch))
ch = getchar();
int i = 0; //记录读到从前往后第几位
while (isdigit(ch))
{
x = 10 * x + ch - '0';
if (ch == '0' && !i)
is_zero = true; //如果第一位就是0,说明这个数为0
x %= mod; //取模
ch = getchar();
i++;
}
return x;
}
int main()
{
int t;
scanf("%d", &t);
while (t--)
{
is_zero = false;
int s;
scanf("%d", &s);
int n = read(s - 1); //读入时对s-1取模
if (is_zero && !n)
puts("0"); //特判n=0
else
{
n = (n + s - 2) % (s - 1) + 1;
printf("%d\n", n);
}
}
return 0;
}
鸣谢
Subtask 1
Subtask 2: Jasper08
Subtask 3: idea: Hoshino_kaede,YYHDoggy,code: YYHDoggy
Subtask 4: idea & code: Jasper08