2024牛客暑期多校训练营3

2024牛客暑期多校训练营3

L-Sudoku and Minesweeper_2024牛客暑期多校训练营3 (nowcoder.com)

题意

给一个 9×9 的填好的数独,要求保留其中一部分数字(至少保留 1 个),剩下的替换成”地雷”。
要求满足对于每个数字,与它 8 连通的格子中是地雷的数量恰好等于自身。

思路

选择一个不在边上的8即可,其他地方全改为地雷。

代码

#include <bits/stdc++.h>

using namespace std;

using i64 = long long;

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    vector<string> s(9);
    for (auto &i : s)
        cin >> i;

    for (int i = 0; i < 9; i ++) {
        for (int j = 0; j < 9; j ++) {
            if (i && j && i != 8 && j != 8 && s[i][j] == '8') {
                for (auto &c : s)
                    c = string(9, '*');
                s[i][j] = '8';
                for (auto &c : s)
                    cout << c << '\n';
                return 0;
            }
        }
    }

    return 0;
}

B-Crash Test_2024牛客暑期多校训练营3 (nowcoder.com)

题意

给定 n 类操作,每次可将 \(x\) 更新为 \(|x-a_i|\)

求从 D 开始可得到的最小值

思路

考虑只选择一类操作,则最终得到的答案即是 \(\min(D\%h_i,h_i-D\%h_i)\)

根据数论中的裴蜀定理,即设 \(a,b\) 是不全为零的整数,对任意整数 \(x,y\) 满足 \(\gcd(a,b)|(ax+by)\),且存在整数 \(x,y\) 使得 \(ax+by=\gcd(a,b)\)

假设对于 \(h_1,h_2\dots,h_n\) 用了 \(x_1,x_2\dots,x_n\) 次操作, 那么对于 \(h_1x_1+h_2x_2+\dots+h_nx_n\) 一定存在一个 \(x\) 序列使得其等于 \(\gcd(h_1,h_2\dots,h_n)\)

所以最终答案就是 \(\min(D\%\gcd_h,\gcd_h-D\%\gcd_h)\)

代码

#include <bits/stdc++.h>

using namespace std;

using i64 = long long;

int main() {
   ios::sync_with_stdio(false);
   cin.tie(nullptr);

   i64 n, D;
   cin >> n >> D;

   i64 g = 0;
   while (n--) {
      i64 h;
      cin >> h;

      g = gcd(h, g);
   }

   cout << min(g - D % g, D % g) << '\n';

   return 0;
}

A-Bridging the Gap 2_2024牛客暑期多校训练营3 (nowcoder.com)

题意

n 个人在河的一侧,第 i 个人有体力值 \(h_i\)

有且仅有一艘能容纳 L 到 R 个人的船

问是否能将所有人从河的左侧运送到右侧

思路

感觉题解讲得很好了

需要从河的右侧往回运送的趟数最小值:\(S=\lceil\frac{n-R}{R-L}\rceil\)

\(a_i=\lfloor\frac{h_i-1}{2}\rfloor\) 为第 i个人能多来回的趟数

一个必要条件是 \(\sum\limits_{i=1}^n\min(a_i,S)≥SL\)

下述贪心保证条件的充分性:每次将体力值最高的若干个人从一侧运输
到另一侧。

贪心成立性的原因在于每次从右侧往回运的过程相当于每次将
\(a_1, a_2, . . . , a_n\)中最大的 L个元素减一,更新后的数组 \(a'_i\) 满足 \(\sum\limits_{i=1}^n\min(a'_i,S-1)≥(S-1)L\) 。由数学归纳法可知正确性。

代码

#include <bits/stdc++.h>

using namespace std;

using i64 = long long;

int main() {
   ios::sync_with_stdio(false);
   cin.tie(nullptr);

   int n, l, r;
   cin >> n >> l >> r;

   i64 S = ceil(1.0 * (n - r) / (r - l));
   i64 ans = 0;

   while (n--) {
      int h;
      cin >> h;

      ans += min(S, 1ll * (h - 1) / 2);
   }

   puts(ans >= S * l ?  "Yes" : "No");

   return 0;
}

J-Rigged Games_2024牛客暑期多校训练营3 (nowcoder.com)

题意

两队打比赛,大比分 2b − 1 赢,小比分 2a − 1 赢。
给定的长度为 n 的串,两队比赛的每个小分结果是这个串的循环重复。
问从该串的每个位置开始,最终谁会赢得整个比赛。

思路

倍增。

首先对于每个位置,计算出它 \(2a-1\) 局后的比分的比分终点的位置。

然后采用倍增,即假设我们要从 \(j\)\(2^i\) 步,可以先从 \(j\)\(2^{i-1}\) 步,然后从跳 \(2^{i-1}\) 的终点也就是 \(jump_{2^{i-1},j}\) 再跳 \(2^{i-1}\) 步,即 \(jump_{i,j} = jump_{i-1,jump_{i-1},j}\)

同理,从 \(j\)\(2^i\) 步的得分我们也要累计起来,但这个时候要统计两部分的 a 小局比分,即 \(j\sim j+2^{i-1}\)\(j+2^{i-1}\sim j+2^i\) 的。

最终判断的时候每次从 \(2^{17}\) 往后跳到凑出 \(2b-1\) 的比分即可。(\(2^{17}>1e5\))

代码

#include <bits/stdc++.h>

using namespace std;

using i64 = long long;

int main() {
   ios::sync_with_stdio(false);
   cin.tie(nullptr);

   int n, a, b;
   cin >> n >> a >> b;

   vector jump(18, vector<int>(n));
   vector sum(18, vector<int>(n));

   string s;
   cin >> s;

   int sc[2] {}, r = 0;
   for (int i = 0; i < n; i ++) {
      while (sc[1] < a && sc[0] < a) {
         sc[s[r] - '0']++;
         r = (r + 1) % n;
      }
      jump[0][i] = r;
      sum[0][i] = sc[1] > sc[0];
      sc[s[i] - '0']--;
   }

   for (int i = 1; i < 18; i ++) {
      for (int j = 0; j < n; j ++) {
         jump[i][j] = jump[i - 1][jump[i - 1][j]];
         sum[i][j] = sum[i - 1][j] + sum[i - 1][jump[i - 1][j]];
      }
   }

   int pos, res, ans;
   for (int i = 0; i < n; i ++) {
      pos = i, res = 0, ans = 0;
      for (int j = 17; j >= 0; j --) {
         if (res + (1 << j) <= 2 * b - 1) {
            res += 1 << j;
            ans += sum[j][pos];
            pos = jump[j][pos];
         }
      }
      cout << (ans >= b);
   }

   return 0;
}

D-Dominoes!_2024牛客暑期多校训练营3 (nowcoder.com)

题意

\(n\) 张骨牌,每个骨牌上写有 \(2\) 个数字 \(x_i, y_i\)
要求将 \(n\) 张骨牌横着摆成一排,一共 \(2n\) 个数字,保证对于相邻的不属于
同一骨牌的数字互不相同。

思路

感觉有点模拟的意味。

对于每一对 \((x,y)[x\ne y]\),如果当前要放得 \(x\) 与前一次的 \(y\) 相同,那就调换顺序。

如果当前的 \(x=y\) ,那我们把这个结果存起来,碰到下一次又有类似的就计数一下或者两者分开放,就类似上一个结论,只不过是把 \((x,y)\) 抽象成了 \(((x_1,y_1),(x_2,y_2))[x_1=y_1,x_2=y_2]\)

最后如果还存在多余的 \((x,y)[x=y]\),那就在之前已经确定好了的顺序中插空放。

注意判断最前面和最后面。

代码

#include <bits/stdc++.h>

using namespace std;

using i64 = long long;

int main() {
   ios::sync_with_stdio(false);
   cin.tie(nullptr);

   int n;
   cin >> n;

   int has = 0, num = 0;
   vector<int> a, b;
   for (int i = 0; i < n; i ++) {
      int x, y;
      cin >> x >> y;
      if (x != y) {
         if (b.size() && b.back() == x) {
            a.emplace_back(y);
            b.emplace_back(x);
         } else {
            a.emplace_back(x);
            b.emplace_back(y);
         }
      } else {
         if (!num || has == x) {
            has = x;
            num ++;
         } else {
            if (!b.size() || b.back() == x) {
               a.emplace_back(has);
               b.emplace_back(has);
               a.emplace_back(x);
               b.emplace_back(x);
            } else {
               a.emplace_back(x);
               b.emplace_back(x);
               a.emplace_back(has);
               b.emplace_back(has);
            }
            num --;
         }
      }
   }

   vector<pair<int, int>> ans;
   for (int i = 0; i < a.size(); i ++) {
      if (num && i && a[i] != b[i - 1] && a[i] != has && b[i - 1] != has) {
         ans.emplace_back(has, has);
         num --;
      }
      if (num && !i && a[i] != has) {
         ans.emplace_back(has, has);
         num --;
      }
      ans.emplace_back(a[i], b[i]);
   }

   if (num && (!b.size() || b.back() != has)) {
      ans.emplace_back(has, has);
      num --;
   }

   if (num) {
      cout << "No\n";
   } else {
      cout << "Yes\n";
      for (auto &[x, y] : ans)
         cout << x << ' ' << y << '\n';
   }

   return 0;
}
posted @ 2024-07-29 21:08  Ke_scholar  阅读(34)  评论(0编辑  收藏  举报