2024-09-24 模拟赛总结

\(0+30+0+0=30\),挂惨了。
比赛链接:http://172.45.35.5/d/HEIGETWO/homework/66f10eb944f0ed11b057157e
http://yl503.yali.edu.cn/d/HEIGETWO/homework/66f10eb944f0ed11b057157e

A - 依依寺

题意:

现在有 \(a\)\(0\)\(b\)\(1\)\(c\)\(2\),有两个人 F 和 S,F 先手,每次操作选择一个数,若所有操作累计的和 \(s\)\(3\) 的倍数,那么操作的那一方输,如果某一方不能操作,那一方也输。两人轮流操作,若两人都已最优操作操作,求谁必赢。

思路:

显然先手不能先选 \(0\),如果不考虑 \(0\),那么只可能如下选择:

  • \(1-1-2-1-2-\cdots\)
  • \(2-2-1-2-1-\cdots\)

在考虑 \(0\) 的情况:选择 \(0\) 只会改变先后手关系,所以只需要考虑 \(a\) 的奇偶性。

\(a\) 为偶数,那么 \(0\) 就没有用,根据上面的性质,当 \(a,b\ge 2\) 时,F 一定能赢,边角情况随便讨论一下即可。

\(a\) 为奇数,那么 \(0\) 指挥改变先后手关系,考虑 \(a,b\) 较大且 \(|a-b|\) 较大的情况。

  • 若 F 先拿元素较少的一堆,接下来就是轮流选 \(12\),S 可以取一个 \(0\),这样 S 就赢了。
  • 若 F 先拿元素叫多的一堆,接下来就是轮流选 \(12\),S 肯定不会取 \(0\),否则 S 就输了,但是 F 可以选 \(0\),那么 F就赢了。

综上,这种情况的 F 必赢。边角情况在代码中给出。

代码:

#include <bits/stdc++.h>

using namespace std;

int T;
long long a, b, c;

bool C(long long a, long long b, long long c) {
  if (a % 2) {
    if (b == 0) {
      return c && c != 1;
    } else {
      return b - 1 > c || c - 1 > b;
    }
  } else {
    if (b == 0) {
      return c == 1;
    } else if (b == 1) {
      return 1;
    } else {
      return c;
    }
  }
}

int main() {
  freopen("yiyi.in", "r", stdin);
  freopen("yiyi.out", "w", stdout);
  ios::sync_with_stdio(0), cin.tie(0);
  for (cin >> T; T; T--) {
    cin >> a >> b >> c;
    cout << (C(a, b, c) ? "First\n" : "Second\n");
  }
  return 0;
}

B - 武义寺

题意:

给定 \(n\),随机生成一个排列 \(p_n\),定义 \(val(p)\)\(p\) 中的最小的 \(i\) 使得 \(i>p_i\),求 \(val(p)\) 的期望。

思路:

由于 \(p\) 是随机的所以只需要求所有 \(val(p)\) 的和在除以 \(n!\) 即可。考虑下面这个图。

从左往右第 \(i\) 列,从下往上第 \(j\) 行涂黑表示 \(p_i=j\),若 \(p\) 为排列,那么每行每列都只能有一个格子涂黑,\(val(p)\) 就是从左往右第一个涂黑格子在红色线下面的列数。

设第一个这种涂黑格子在第 \(i\) 处,那么我们就需要求出前 \(i-1\) 列都在红色格子上面的涂法。考虑枚举第 \(i\) 列的黑色格子涂在哪里,若 \(p_i=j\),那么数量就是 \((n-i+2)^{i-j-1}\times(n-i+1)^j\times(n-i)!\)\((n-i+2)^{i-j-1}\) 表示 \(j+1\)\(i-1\) 所有方案的乘积,\((n-i+1)^j\) 表示 \(1\)\(j\) 所有方案的乘积,\((n-i)!\) 表示 \(i+1\)\(n\) 随便放。令 \(k=n+i+1\),再累加起来,推推式子:

\[\sum_{j=1}^{i-1} ((k+1)^{i-j-1}\times k^j\times(n-i)!)=(n-i)!(k+1)^{i-1}\sum_{j=1}^{i-1} (\frac{k}{k+1})^j=((k+1)^{i-1}-k^{i-1})\times k\times (n-i)! \]

那么总数就是 \(\displaystyle\sum_{i=1}^n (((n-i+2)^{i-1}-(n-i+1)^{i-1})\times(n-i+1)\times(n-i)!)+n+1\),要加 \(n+1\) 是因为可能不合法。

代码:

#include <bits/stdc++.h>

using namespace std;

const int kMaxN = 1e6 + 5, kM = 998244353;

int n;
long long f[kMaxN], ans;

long long fpow(long long a, long long b, long long p, long long r = 1) {
  for (a %= p; b; b & 1 && (r = r * a % p), b >>= 1, a = a * a % p) {
  }
  return r;
}

int main() {
  freopen("wuyi.in", "r", stdin);
  freopen("wuyi.out", "w", stdout);
  cin >> n, f[0] = 1;
  for (int i = 1; i <= n; i++) {
    f[i] = f[i - 1] * i % kM;
  }
  for (int i = 1; i <= n; i++) {
    ans = (ans + i * f[n - i + 1] % kM * (fpow(n - i + 2, i - 1, kM) - fpow(n - i + 1, i - 1, kM)) % kM) % kM;
  }
  cout << (ans + n + 1 + kM) * fpow(f[n], kM - 2, kM) % kM;
  return 0;
}

C - 依久依久

题意:

对于一个正整数 \(x\),那么 \(x\) 可以表示成多个斐波那契数之和,若这些斐波那契数互不相同且不相邻,那么这种分解是唯一的。

形式化的说,\(x=\sum_{i=1}^k fib_{a_i},a_i>a_{i-1}+1\),这种分解时唯一的,设 \(val(x)=\displaystyle\bigoplus_{i=1}^k fib_{a_i}\)。给定 \(l,r\),求 \(\displaystyle\bigoplus_{i=l}^r val(i)\)

思路:

首先考虑用前缀和将题目转化成求 \(S(x)=\displaystyle\bigoplus_{i=1}^x val(i)\),先想一想可以如何构造,对于 \(x\),可以找到最大的小于等于 \(x\) 的斐波那契数,然后将 \(x\) 减去这个数,一直这样递归即可。这样为什么不会出现相邻的斐波那契数呢?因为如果出现相邻的斐波那契数,那么 \(x\ge fib_i+fib_{i+1}=fib_{i+2}\),那么最大的小于等于 \(x\) 的数就不是 \(fib_i\) 了。

考虑如下计算 \(S(x)\)

\[S(x)=S(fib_i-1)\bigoplus S(x-fib_i)\bigoplus ((x-fib_i+1)\bmod 2)\times fib_i \]

解释一下这个式子,先找到第一个小于等于 \(x\) 的斐波那契数,那么 \(fib_i\)\(x\) 都要选择 \(fib_i\),并且要减去 \(fib_i\),所以要异或 \(S(x-fib_i)\bigoplus ((x-fib_i+1)\bmod 2)\times fib_i\),那么小于 \(fib_i\) 的数重新算即可。

由于 \(fib_i\)\(2^i\) 的值域差不多,每次减去 \(fib_i\),那么时间复杂度就是 \(\log W\)\(W\) 是值域大小,加上记忆化搜索就可以过了。

代码:

#include <bits/stdc++.h>

using namespace std;

const int kMaxN = 90;

int T;
long long l, r, fib[kMaxN], ans;
map<long long, long long> mp;

long long f(long long x) {
  if (mp.count(x)) {
    return mp[x];
  }
  int p = upper_bound(fib, fib + kMaxN, x) - fib - 1;
  return mp[x] = f(fib[p] - 1) ^ f(x - fib[p]) ^ ((x - fib[p] + 1) % 2 ? fib[p] : 0);
}

int main() {
  freopen("yijiu.in", "r", stdin);
  freopen("yijiu.out", "w", stdout);
  ios::sync_with_stdio(0), cin.tie(0);
  fib[0] = fib[1] = 1, mp[0] = 0, mp[1] = 1;
  for (int i = 2; i < kMaxN; i++) {
    fib[i] = fib[i - 1] + fib[i - 2];
  }
  for (cin >> T; T; T--, ans = 0) {
    cin >> l >> r;
    cout << (f(r) ^ f(l - 1)) << '\n';
  }
  return 0;
}

D - 补幺梨

题意:

\(n\) 种货币,面值分别为 \(a_1,a_2,\cdots,a_n\),每种货币你都有无限张,求最大的凑不出来的价格(不能找钱)。

思路:

\(a_0=\displaystyle\min_{i=1}^n a_i\),考虑在模 \(a_0\) 的系统下,若 \(0\le r<a_0\) 存在,那么 \(r+n\times a_0\) 也能凑成,我们用最短路求出能凑出的 \(x\bmod a_0=r\) 的最小的 \(x\),那么 \(x-a_0\) 一定没有被凑到,否则 \(x\) 就不是最小的了,所以只需要求出能到余数 \(r\) 的最小的 \(x\) 减去 \(a_0\) 在去最大值即可。

这种方法叫做同余最短路。

代码:

#include <bits/stdc++.h>

using namespace std;

const int kMaxN = 1e7 + 5, kMaxM = 1e8 + 5;

int n, m, a[kMaxN], mod;
long long dis[kMaxM], ans = -1;
bitset<kMaxM> vis;

void dijkstra(int s) {
  priority_queue<pair<long long, int>> q;
  for (fill(dis, dis + mod, 1e18), vis = 0, q.push({dis[s] = 0, s}); q.size(); ) {
    int u = q.top().second;
    q.pop();
    if (vis[u]) {
      continue;
    }
    vis[u] = 1;
    for (int i = 1, v, w; i <= n; i++) {
      v = (u + a[i]) % mod, w = a[i];
      if (dis[v] > dis[u] + w) {
        q.push({-(dis[v] = dis[u] + w), v});
      }
    }
  }
}

int main() {
  freopen("pear.in", "r", stdin);
  freopen("pear.out", "w", stdout);
  ios::sync_with_stdio(0), cin.tie(0);
  cin >> n >> m;
  for (int i = 1; i <= n; i++) {
    cin >> a[i];
  }
  mod = *min_element(a + 1, a + 1 + n), dijkstra(0);
  for (int i = 0; i < mod; i++) {
    dis[i] != 1e18 && (ans = max(ans, dis[i] - mod));
  }
  cout << ans;
  return 0;
}
posted @ 2024-09-24 22:23  liruixiong0101  阅读(12)  评论(0编辑  收藏  举报