「解题报告」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% 的数据,n10

对于 50% 的数据,n1000

对于 90% 的数据n1015

对于 100% 的数据,1n1010001T10

50 分暴力#

先判断是否必输,如果必输的话直接输出 0 即可。

否则,n2 枚举第一次在哪个堆里拿走多少个石子,查看剩下的石子异或和是否为 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 为偶数时,xxor(x+1)=1,可以发现 1n 的异或和 S 有以下规律:

S={1,nmod4=1n+1,nmod4=20,nmod4=3n,nmod4=0

nmod4=3 时,先手必败,答案为 0nmod4=1 时,在任意编号为奇数的石子堆中取 1 可以使异或和为 0,且只有这些方案,于是答案为 (n+1)2nmod4=02 时,Sn 位数相同,设它们的最高位为 tS 的第 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。一
个数字称之为“幸运数”当且仅当它只由 ab 组成。小林称一个数为“魔数”,当且仅当这个数各数位之和为“幸运数”,且这个数字本身也是“幸运数”。

举个例子:小林的幸运数字为 2,亮亮的幸运数字为 6,那么 23 不是“幸运数”,而 26222 是“幸运数”。进一步,222 是“魔数”(因为 2+2+2=6),而 26 不是“魔数”(因为 2+6=8 )。

亮亮想要知道,有多少个 n 位的“魔数”(一个 n 位数不包含前导 0),由于这个数字会很大,所以亮亮只关心这个数模 1000000007109+7,是一个质数)的结果。

【输入格式】

只有一行,包含三个整数:abn。意义见题目描述。

【输出格式】

只有一个整数,表示 n 位的“魔数”的个数模1000000007 的结果。

【样例输入】

2 6 3

【样例输出】

1

【样例解释】

两个幸运数字分别为 26,则位数为 3 的“魔数”只有 222 一个。

【数据规模】

对于 30% 的数据: 1n5

对于 60% 的数据:1n100

对于 100% 的数据:1a<b9,1n106

只需枚举这 n 位数中有多少位是 a,设有 i 位,则各个位上的数字的和为 i×a+(ni)×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,而纵坐标却成了 Yi (为了简化问题,我们假设所有的坐标都是整数)。

对此,教官当然十分生气,因此他决定下命令调整队伍,使得所有人能够站
成一条直线(也即让所有的 Yi 相同)。教官的命令总共有三种:

  1. 除了某一个同学外,其余所有同学向前走一步(向前走一步可理解为 Yi
    值加 1,下同);

  2. 除了某一个同学外,其余所有同学向前走两步;

  3. 除了某一个同学外,其余所有同学向前走五步。

教官希望他能以最少的命令次数使得所有同学站成一条直线,但是他不会算,于是就让亮亮帮他来计算。亮亮虽然聪明,可是由于班上同学人数众多,他一下子也解决不了这个问题,只能来寻求会编程的你的帮助,你能告诉亮亮答案吗?

【输入格式】

第一行有一个整数 n,表示班上共有 n 位同学。

第二行有 n 个整数,第 i 个整数 Yi 表示第 i 位同学初始位置的纵坐标。

【输出格式】

一个整数,表示最少的下达命令次数。

【样例输入】

4
1 1 2 6

【样例输出】

2

【样例解释】

一种方案是:1 1 2 62 2 2 77 7 7 7

【数据规模】

对于 40% 的数据,n10Yi10

对于 100% 的数据,1n1000000Yi109

除了这个同学,其他人都往前走 1 步、2 步、5 步,用物理上的相对运动,可以将其看作其他人都不动,这个同学往后走 1 步、2 步、5 步,将 Y 从小到大排序,则他们最后会落在 Ymin,Ymin1,Ymin2,Ymin3,Ymin45 个位置上,枚举这 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;
}

作者:yifan0305

出处:https://www.cnblogs.com/yifan0305/p/17769095.html

版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。

转载时还请标明出处哟!

posted @   yi_fan0305  阅读(19)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
more_horiz
keyboard_arrow_up light_mode palette
选择主题
menu
点击右上角即可分享
微信分享提示