首页 > 其他分享 >3 月 19 日测试题解

3 月 19 日测试题解

时间:2023-03-19 16:33:40浏览次数:58  
标签:std cur 测试题 19 text ++ i64 int

3 月 19 日测试题解

原来这就是 AK 的滋味吗,不过,我却完全没感到开心呢。

T1

题意

给出两个整数 \(a\),\(b\),重复以下操作直到 \(a = b\):

  • 设 \(a > b\),否则交换 \(a\) 与 \(b\),然后让 \(a\) 减去 \(b\)。

输出操作次数。

\(a, b \le 10^{18}\)。

思路

\(\text{100pts}\)

毫无难度的题目,其实就是在模拟辗转相除法,一样的方法再加个计数就行了,只不过我没写递归的。

代码

\(\text{100pts}\)

#include <bits/stdc++.h>

using i64 = long long;

void solve() {
  i64 a, b;
  std::cin >> a >> b;
  
  i64 ans = 0;
  while (a != b) { 
    if (a < b) {
      std::swap(a, b);
    }
    if (a % b == 0) {
      ans += a / b - 1;
      break;
    }
    ans += a / b;
    a -= a / b * b;
  }
  std::cout << ans << "\n";
  
  return;
}

int main() {
  std::ios::sync_with_stdio(false);
  std::cin.tie(0);
  
  int t;
  std::cin >> t;
  
  while (t--) {
    solve();
  }
  
  return 0;
}

T2

题意

给出五个包含 \(n\) 个元素的数组,问是否存在一种方法,从每个数组中选出一个数并使得五个数之和等于 \(0\)。

对于 \(30\%\) 的数据,\(n \le 20\);
对于 \(100\%\) 的数据,\(n \le 200\),数组中元素的最大值不超过 \(10^8\)。

思路

\(\text{30pts}\)

暴力枚举,时间复杂度为 \(O(n^5)\)。

\(\text{100pts}\)

话说折半都考第几次了。

考虑折半搜索,先计算出前边两个数组的和,再算后边两个的,然后枚举一下就做完了,我用的哈希表,期望的时间复杂度为 \(O(n^3)\)。

代码

\(\text{100pts}\)

#include <bits/stdc++.h>

using i64 = long long;

int main() {
  std::ios::sync_with_stdio(false);
  std::cin.tie(0);
  
  int n;
  std::cin >> n;
  
  std::vector<std::vector<int>> a(5, std::vector<int>(n));
  for (int i = 0; i < 5; i++) {
    for (int j = 0; j < n; j++) {
      std::cin >> a[i][j];
    }
  }
  
  std::unordered_set<int> sum1, sum2;
  for (int i = 0; i < n; i++) {
    for (int j = 0; j < n; j++) {
      int cur = a[0][i] + a[1][j];
      sum1.insert(cur);
    }
  }
  for (int i = 0; i < n; i++) {
    for (int j = 0; j < n; j++) {
      int cur = a[2][i] + a[3][j];
      sum2.insert(cur);
    }
  }
  
  for (auto i : sum1) {
    for (auto j : sum2) {
      for (auto k : a[4]) {
        if (i + j + k == 0) {
          std::cout << "YES\n";
          return 0;
        }
      }
    }
  }
  std::cout << "NO\n";
  
  return 0;
}

T3

题意

给定一个长度为 \(n\) 的序列 \(a\),你可以任意选取序列中的两个相同数字 \(i\) ,将它们删去后,将其合并为 \(2i\) 后再加入序列,问有多少个 \(a\) 的子序列满足通过合并能够得到 2048。

对于 \(40\%\) 的数据,\(n \le 20\);
对于 \(100\%\) 的数据,\(n \le 10^5\),\(a_i \le 2048\)。

思路

\(\text{40pts}\)

我们考虑二进制枚举子序列在判断其能不能产生 2048。

如何快速判断呢?首先意识到不是 2 的幂的数字并不重要,因为它们无论怎么合并都不可能产生 2048,因此我们只需要记录它们的个数就好了。将这些多余的数字删去后,我们可以这样想:合成 2048 一共需要 \(2^{11}\) 个 1,那么是不是只要一个子序列的和超过了 \(2^{11}\),就一定可以合并出 2048 呢?

更一般地,我们有命题如下:

任意地给定一个只包含 \(2^i\) 的序列 \(a\),若 \(\sum_{j \in a}{j} \ge 2^k\) 且有 \(\forall j \in a, j \lt 2^k\),则可以通过合并的方式得到 \(2^k\),其中 \(i, k \in \mathbb{N}\)。

只要这个命题是真的,我们就有了一种快速判断序列合法性的方法。事实上,这个命题确实为真。我们可以通过如下方法证明:

显然我们有如下结论:

对于一个只包含 \(2^i\) 的序列 \(a\),若 \(\sum_{j \in a}j \ge 2^k\) 且有 \(\forall j \in a, j \lt 2^k\),则至少存在一对 \(m, n\) 使得 \(a_m = a_n\)。

通过 \(\sum_{i = 0}^{n} = 2^{n + 1} - 1\),这个结论可以证明。

那么显然,\(a_m\) 与 \(a_n\) 是可以合成的,设 \(m\) 与 \(n\) 是满足条件且 \(a_m\) 最小的一对,我们期望的是产生连锁反应,即可以一直向上合成。会不会出现无法再向上合成的情况呢?其实是不会的。因为如果不能向上合成,那么只有两种情况:

  1. \(\sum_{j \in a} j < 2^k\)
  2. 至少存在一对 \(p\) 与 \(q\) 使得 \(a_p = a_q\) 并且 \(a_p \ge a_m\)。

第二种情况是好说的,如果存在这么一对 \(p\) 与 \(q\) 的话,那你为什么还要从 \(m\) 与 \(n\) 开始向上合成呢?这是不优的。因此,我们可以将 \(a_p\) 与 \(a_q\) 作为新的起点向上合成。

而为什么只有这两种情况呢?因为 \(a_p \ge a_m\),所以如果不存在这样的 \(p\) 与 \(q\) 并且还有数字缺失的话,那么肯定是不符合 \(\sum_{j \in a}j \ge 2^k\) 的。

那么我们可以通过这种方式得到 \(2^k\)。

证明思路很混乱,看看就行了,没必要深究。

那么我们只需要枚举出所有的子序列,就可以拿到 \(\text{40pts}\) 了,哦对了,不要忘掉被删掉的数字。

\(\text{100pts}\)

上边的思路是没错的,我们需要把枚举的部分换成一个 01 背包。

这里有一个小技巧,如果直接算大于等于 2048 的个数不是很好算,但是我们可以先把小于 2048 的个数算出来,总的子序列个数为 \(2^n\),然后做下减法就好了,时间复杂度为 \(O(2048n)\),可以接受。

代码

\(\text{100pts}\)

#include <bits/stdc++.h>

using i64 = long long;

i64 fastPow(i64 base, i64 pow, i64 mod) {
  i64 res = 1;
  while (pow) {
    if (pow & 1) {
      (res *= base) %= mod;
    }
    (base *= base) %= mod;
    pow >>= 1;
  }
  return res;
}

int main() {
  std::ios::sync_with_stdio(false);
  std::cin.tie(0);
  
  static const int MOD = 998244353, M = 1 << 11;
  
  int n;
  std::cin >> n;
  
  std::vector<int> a;
  int rst = 0;
  for (int i = 0; i < n; i++) {
    int cur;
    std::cin >> cur;
    if (__builtin_popcount(cur) == 1) {
      a.push_back(cur);
    } else {
      rst++;
    }
  }
  
  if (!a.size()) {
    std::cout << 0 << "\n";
    return 0;
  }
  
  std::vector<int> f(M + 1);
  f[0] = 1;
  for (int i = 0; i < a.size(); i++) {
    for (int j = M; j >= a[i]; j--) {
      (f[j] += f[j - a[i]]) %= MOD;
    }
  }
  for (int i = 1; i <= M; i++) {
    (f[i] += f[i - 1]) %= MOD;
  }
  
  i64 ans = (fastPow(2, a.size(), MOD) - f[M - 1] + MOD) % MOD;
  (ans *= fastPow(2, rst, MOD)) %= MOD;
  std::cout << ans << "\n";
  
  return 0;
}

标签:std,cur,测试题,19,text,++,i64,int
From: https://www.cnblogs.com/forgot-dream/p/17233497.html

相关文章