「解题报告」2023-10-16 模拟赛
1、取石子游戏
(nim.pas/c/cpp)
【题目描述】
小林和亮亮正在玩一个取石子的游戏。
石子一共有 \(n\) 堆,其中第 \(i\) 堆恰好有 \(i\) 粒石子。小林先取,亮亮后取,并且两人依次轮流取石。每一次取石子的人可以选择任意一堆还未被取完的石子,并取走这一堆中任意多粒石子(注意,不能一粒石子也不取,也不能同时在多堆石
子中取石)。最终,无石可取的人为败。
小林和亮亮都十分聪明,他们的每次取石都会采取最优策略。在经过多次游戏后,小林发现了先手必胜的条件,但他不满足于此,他想知道,在知道石子的堆数 \(n\) 后,他第一次取石有多少种方式可以获胜。
【输入格式】
第一行一个整数 \(T\),表示数据组数。
接下来 \(T\) 行每行一个整数 \(n\),表示石子的堆数。
【输出格式】
每组数据输出一个整数,表示在两个人都采用最佳策略的情况下,小林第一次取石能够获胜的方式数,如小林必败,则输出 \(0\)。
【样例输入】
2
2
3
【样例输出】
1
0
【样例解释】
\(n=2\) 时小林只有一种取胜方式,即取在第二堆石子中取一粒。
\(n=3\) 时小林必败,因此输出 \(0\)。
【数据规模】
对于 \(20\%\) 的数据,\(n \le 10\);
对于 \(50\%\) 的数据,\(n \le 1000\);
对于 \(90\%\) 的数据\(n \le 10^{15}\);
对于 \(100\%\) 的数据,\(1 \le n \le 10^{1000}\),\(1 \le T \le 10\)。
\(50\) 分暴力
先判断是否必输,如果必输的话直接输出 \(0\) 即可。
否则,\(n^2\) 枚举第一次在哪个堆里拿走多少个石子,查看剩下的石子异或和是否为 \(0\)。
// The code was written by yifan, and yifan is neutral!!!
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define bug puts("NOIP rp ++!");
#define rep(i, a, b, c) for (int i = (a); i <= (b); i += (c))
#define per(i, a, b, c) for (int i = (a); i >= (b); i -= (c))
template<typename T>
inline T read() {
T x = 0;
bool fg = 0;
char ch = getchar();
while (ch < '0' || ch > '9') {
fg |= (ch == '-');
ch = getchar();
}
while (ch >= '0' && ch <= '9') {
x = (x << 3) + (x << 1) + (ch ^ 48);
ch = getchar();
}
return fg ? ~x + 1 : x;
}
template<typename T>
void write(T x) {
if (x < 0) {
putchar('-');
x = -x;
}
if (x > 9) {
write(x / 10);
}
putchar(x % 10 + '0');
}
template<typename T>
void print(T x, char c) {
write(x);
putchar(c);
}
const int N = 1010;
int T;
ll n;
ll a[N];
void solve() {
n = read<ll>();
int cnt = 0;
ll sum = 0;
for (ll i = 1; i <= n; ++ i) {
a[i] = a[i - 1] ^ i;
sum ^= i;
}
if (sum == 0) {
puts("0");
return ;
}
rep (i, 1, n, 1) {
rep (j, 1, i, 1) {
ll k = sum;
k ^= i;
k ^= (i - j);
if (k == 0) {
++ cnt;
}
}
}
print(cnt, '\n');
}
int main() {
// freopen("nim.in", "r", stdin);
// freopen("nim.out", "w", stdout);
T = read<int>();
while (T --) {
solve();
}
fclose(stdin);
fclose(stdout);
return 0;
}
\(90\) 分做法
如果 \(x\) 为偶数时,\(x \operatorname{xor} (x + 1) = 1\),可以发现 \(1 \sim n\) 的异或和 \(S\) 有以下规律:
\(n \bmod 4 = 3\) 时,先手必败,答案为 \(0\)。\(n \bmod 4 = 1\) 时,在任意编号为奇数的石子堆中取 \(1\) 可以使异或和为 \(0\),且只有这些方案,于是答案为 \(\dfrac{(n + 1)}{2}\)。\(n \bmod 4 = 0 或 2\) 时,\(S\) 与 \(n\) 位数相同,设它们的最高位为 \(t\),\(S\) 的第 \(t\) 位为 \(1\),为使 \(S\) 变为 \(0\),必须修改一个第 \(t\) 位为 \(1\) 的数。显然修改任意第 \(t\) 位为 \(1\) 的数都可以使 \(S\) 变为 \(0\)。答案即为 \(n\) 去掉最高位再加 \(1\)。
// The code was written by yifan, and yifan is neutral!!!
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define bug puts("NOIP rp ++!");
#define rep(i, a, b, c) for (int i = (a); i <= (b); i += (c))
#define per(i, a, b, c) for (int i = (a); i >= (b); i -= (c))
template<typename T>
inline T read() {
T x = 0;
bool fg = 0;
char ch = getchar();
while (ch < '0' || ch > '9') {
fg |= (ch == '-');
ch = getchar();
}
while (ch >= '0' && ch <= '9') {
x = (x << 3) + (x << 1) + (ch ^ 48);
ch = getchar();
}
return fg ? ~x + 1 : x;
}
template<typename T>
void write(T x) {
if (x < 0) {
putchar('-');
x = -x;
}
if (x > 9) {
write(x / 10);
}
putchar(x % 10 + '0');
}
template<typename T>
void print(T x, char c) {
write(x);
putchar(c);
}
const int N = 1010;
int T;
ll n;
ll a[N];
void solve1() {
n = read<ll>();
int cnt = 0;
ll sum = 0;
for (ll i = 1; i <= n; ++ i) {
a[i] = a[i - 1] ^ i;
sum ^= i;
}
if (sum == 0) {
puts("0");
return ;
}
rep (i, 1, n, 1) {
rep (j, 1, i, 1) {
ll k = sum;
k ^= i;
k ^= (i - j);
if (k == 0) {
++ cnt;
}
}
}
print(cnt, '\n');
}
ll get(ll x) {
int cnt = 0;
while (x) {
x >>= 1;
++ cnt;
}
return (1ll << (cnt - 1));
}
void solve2() {
n = read<ll>();
if (n % 4 == 3) {
puts("0");
return ;
}
if (n % 4 == 1) {
print((n + 1) / 2, '\n');
return ;
}
if (n % 4 == 2 || n % 4 == 0) {
ll g = get(n);
print(n - g + 1, '\n');
}
}
int main() {
// freopen("nim.in", "r", stdin);
// freopen("nim.out", "w", stdout);
T = read<int>();
while (T --) {
solve2();
}
fclose(stdin);
fclose(stdout);
return 0;
}
2、魔数
(number.pas/c/cpp)
【题目描述】
小林和亮亮是好朋友。小林有一个幸运数字 \(a\),亮亮有一个幸运数字 \(b\)。一
个数字称之为“幸运数”当且仅当它只由 \(a\) 和 \(b\) 组成。小林称一个数为“魔数”,当且仅当这个数各数位之和为“幸运数”,且这个数字本身也是“幸运数”。
举个例子:小林的幸运数字为 \(2\),亮亮的幸运数字为 \(6\),那么 \(23\) 不是“幸运数”,而 \(26、222\) 是“幸运数”。进一步,\(222\) 是“魔数”(因为 \(2+2+2=6\)),而 \(26\) 不是“魔数”(因为 \(2+6=8\) )。
亮亮想要知道,有多少个 \(n\) 位的“魔数”(一个 \(n\) 位数不包含前导 \(0\)),由于这个数字会很大,所以亮亮只关心这个数模 \(1000000007\)(\(10^9+7\),是一个质数)的结果。
【输入格式】
只有一行,包含三个整数:\(a、b、n\)。意义见题目描述。
【输出格式】
只有一个整数,表示 \(n\) 位的“魔数”的个数模\(1000000007\) 的结果。
【样例输入】
2 6 3
【样例输出】
1
【样例解释】
两个幸运数字分别为 \(2\) 和 \(6\),则位数为 \(3\) 的“魔数”只有 \(222\) 一个。
【数据规模】
对于 \(30\%\) 的数据: \(1 \le n \le 5\);
对于 \(60\%\) 的数据:\(1 \le n \le 100\);
对于 \(100\%\) 的数据:\(1 \le a < b \le 9, 1 \le n \le 10^6\)。
只需枚举这 \(n\) 位数中有多少位是 \(a\),设有 \(i\) 位,则各个位上的数字的和为 \(i \times a + (n - i) \times b\),再进行判断即可,复杂度 \(O(6n)\)。(大概)唯一会的题
// The code was written by yifan, and yifan is neutral!!!
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define bug puts("NOIP rp ++!");
#define rep(i, a, b, c) for (int i = (a); i <= (b); i += (c))
#define per(i, a, b, c) for (int i = (a); i >= (b); i -= (c))
template<typename T>
inline T read() {
T x = 0;
bool fg = 0;
char ch = getchar();
while (ch < '0' || ch > '9') {
fg |= (ch == '-');
ch = getchar();
}
while (ch >= '0' && ch <= '9') {
x = (x << 3) + (x << 1) + (ch ^ 48);
ch = getchar();
}
return fg ? ~x + 1 : x;
}
template<typename T>
void write(T x) {
if (x < 0) {
putchar('-');
x = -x;
}
if (x > 9) {
write(x / 10);
}
putchar(x % 10 + '0');
}
template<typename T>
void print(T x, char c) {
write(x);
putchar(c);
}
const int N = 1e6 + 5;
const int mod = 1e9 + 7;
int a, b, n;
ll sum, ans;
int dig[N];
ll fac[N], inv[N];
ll qpow(ll x, ll y) {
ll res = 1;
while (y) {
if (y & 1) {
res = res * x % mod;
}
y >>= 1;
x = x * x % mod;
}
return res;
}
ll C(ll n, ll m) {
return fac[n] * inv[m] % mod * inv[n - m] % mod;
}
bool check(ll res) {
while (res) {
if (res % 10 != a && res % 10 != b) return false;
res /= 10;
}
return true;
}
void dfs(int u) {
if (u > n) {
if (check(sum)) {
++ ans;
ans %= mod;
}
return ;
}
dig[u] = a;
sum += a;
dfs(u + 1);
sum -= a;
dig[u] = b;
sum += b;
dfs(u + 1);
sum -= b;
}
int main() {
freopen("number.in", "r", stdin);
freopen("number.out", "w", stdout);
a = read<int>(), b = read<int>(), n = read<int>();
if (n <= 5) {
dfs(1);
print(ans % mod, '\n');
return 0;
}
fac[0] = inv[0] = 1;
rep (i, 1, n, 1) {
fac[i] = fac[i - 1] * i % mod;
}
inv[n] = qpow(fac[n], mod - 2);
per (i, n - 1, 1, 1) {
inv[i] = inv[i + 1] * (i + 1) % mod;
}
ll ans = 0;
rep (i, 0, n, 1) {
sum = 1ll * a * i + 1ll * (n - i) * b;
if (check(sum)) {
ans = (ans + C(n, i)) % mod;
}
}
print(ans % mod, '\n');
fclose(stdin);
fclose(stdout);
return 0;
}
3、军训站队
(queue.pas/c/cpp)
【题目描述】
小林和亮亮刚入高中,首先就迎来了军训。这一天,他们班的同学正在教官的指导下进行队列练习。
班上总共有 \(n\) 位同学,他们站成一排,然而,本该站成一条直线的他们却排成了“一字长蛇阵”。用数学的语言来描述,如果把训练场看成一个平面直角坐标系,第 \(i\) 名同学所在位置的横坐标是 \(i\),而所有同学的纵坐标本该是 \(0\)(或者任意一个相等的常量),这样就排成了一条直线。当然,由于同学们排的歪歪扭扭,所以第 \(i\) 位同学的横坐标依然是 \(i\),而纵坐标却成了 \(Y_i\) (为了简化问题,我们假设所有的坐标都是整数)。
对此,教官当然十分生气,因此他决定下命令调整队伍,使得所有人能够站
成一条直线(也即让所有的 \(Y_i\) 相同)。教官的命令总共有三种:
-
除了某一个同学外,其余所有同学向前走一步(向前走一步可理解为 \(Y_i\) 的
值加 \(1\),下同); -
除了某一个同学外,其余所有同学向前走两步;
-
除了某一个同学外,其余所有同学向前走五步。
教官希望他能以最少的命令次数使得所有同学站成一条直线,但是他不会算,于是就让亮亮帮他来计算。亮亮虽然聪明,可是由于班上同学人数众多,他一下子也解决不了这个问题,只能来寻求会编程的你的帮助,你能告诉亮亮答案吗?
【输入格式】
第一行有一个整数 \(n\),表示班上共有 \(n\) 位同学。
第二行有 \(n\) 个整数,第 \(i\) 个整数 \(Y_i\) 表示第 \(i\) 位同学初始位置的纵坐标。
【输出格式】
一个整数,表示最少的下达命令次数。
【样例输入】
4
1 1 2 6
【样例输出】
2
【样例解释】
一种方案是:\(1\ 1\ 2\ 6 \rightarrow 2\ 2\ 2\ 7 \rightarrow 7\ 7\ 7\ 7\)
【数据规模】
对于 \(40\%\) 的数据,\(n \le 10, Y_i \le 10\);
对于 \(100\%\) 的数据,\(1 \le n \le 100000,0 \le Y_i \le 10^9。\)
除了这个同学,其他人都往前走 \(1\) 步、\(2\) 步、\(5\) 步,用物理上的相对运动,可以将其看作其他人都不动,这个同学往后走 \(1\) 步、\(2\) 步、\(5\) 步,将 \(Y\) 从小到大排序,则他们最后会落在 \(Y_{\min}, Y_{\min} - 1, Y_{\min} - 2, Y_{\min} - 3, Y_{\min} - 4\) 这 \(5\) 个位置上,枚举这 \(5\) 个位置,找到最小的步数,最有方案肯定是先走 \(5\),走不动后再走 \(2\),走不动后再走 \(1\),这样可以使步数最小。
// The code was written by yifan, and yifan is neutral!!!
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define bug puts("NOIP rp ++!");
#define rep(i, a, b, c) for (int i = (a); i <= (b); i += (c))
#define per(i, a, b, c) for (int i = (a); i >= (b); i -= (c))
template<typename T>
inline T read() {
T x = 0;
bool fg = 0;
char ch = getchar();
while (ch < '0' || ch > '9') {
fg |= (ch == '-');
ch = getchar();
}
while (ch >= '0' && ch <= '9') {
x = (x << 3) + (x << 1) + (ch ^ 48);
ch = getchar();
}
return fg ? ~x + 1 : x;
}
template<typename T>
void write(T x) {
if (x < 0) {
putchar('-');
x = -x;
}
if (x > 9) {
write(x / 10);
}
putchar(x % 10 + '0');
}
template<typename T>
void print(T x, char c) {
write(x);
putchar(c);
}
const int N = 1e5 + 5;
int n;
ll ans, minn;
ll y[N], x[N];
int main() {
// freopen("queue.in", "r", stdin);
// freopen("queue.out", "w", stdout);
n = read<int>();
rep (i, 1, n, 1) {
y[i] = read<int>();
}
sort(y + 1, y + n + 1);
per (i, n, 1, 1) {
y[i] -= y[1];
}
memcpy(x, y, sizeof x);
rep (j, 0, 4, 1) {
ans = 0;
memcpy(x, y, sizeof y);
rep (i, 1, n, 1) {
x[i] += j;
}
rep (i, 1, n, 1) {
if (x[i] >= 5) {
ans += (1ll * x[i] / 5);
x[i] %= 5;
}
if (x[i] >= 2) {
ans += (1ll * x[i] / 2);
x[i] %= 2;
}
if (x[i] > 0) {
ans += (1ll * x[i]);
x[i] = 0;
}
}
if (j == 0) {
minn = ans;
} else {
minn = min(minn, ans);
}
}
print(minn, '\n');
return 0;
}