牛客周赛 Round 106

写在前面

比赛地址:https://ac.nowcoder.com/acm/contest/116002

无奖竞猜为什么今天在做算法题(

A

显然对于 \(n\bmod 2 = 1\),总格子数量为奇数,用 \(1\times 2\) 的方格一定无解。

然后发现对于 \(n\bmod 2 = 0\) 类似样例地构造一定有解。

//
/*
By:Luckyblock
*/
#include <bits/stdc++.h>
#define LL long long
//=============================================================
//=============================================================
//=============================================================
int main() {
  //freopen("1.txt", "r", stdin);
  std::ios::sync_with_stdio(0), std::cin.tie(0);
  int T = 1;
  while (T --) {
    int n; std::cin >> n;
    std::cout << (n % 2 == 0 ? "YES" : "NO");
  }
  return 0;
}

B

\(k\le 20\),数据范围很小,直接模拟检查是否有解即可。

//
/*
By:Luckyblock
*/
#include <bits/stdc++.h>
#define LL long long
//=============================================================
//=============================================================
bool check(LL x_) {
  LL x = x_;
  std::vector<int> vec;
  while (x) {
    vec.push_back(x % 10);
    x /= 10;
  }
  for (int i = 0, j = vec.size() - 1; i < j; ++ i, -- j) {
    if (vec[i] != vec[j]) return false;
  }
  return true;
}
void add(LL &x_) {
  LL x = x_, y = 0;
  std::vector<int> vec;
  while (x) {
    vec.push_back(x % 10);
    x /= 10;
  }
  for (auto it : vec) {
    y = 10ll * y + it; 
  }
  x_ = x_ + y;
}
//=============================================================
int main() {
  //freopen("1.txt", "r", stdin);
  std::ios::sync_with_stdio(0), std::cin.tie(0);
  int T; std::cin >> T;
  while (T --) {
    LL n, k, ans = -1; std::cin >> n >> k;
    for (int i = 0; i <= k; ++ i) {
      if (check(n)) {
        ans = i;
        break;
      } else if (i < k) {
        add(n);
      }
    }
    std::cout << n << " " << ans << "\n";
  }
  return 0;
}

C

保证 \(a_5\) 之后均合法,显然仅需考虑 \(a_1, a_2, a_3, a_4\) 的关系即可。

又仅需考虑个位数的乘积,则由小学数学可知,构造 \(a_1, a_2\) 时实际上仅需考虑它们的个位数的情况即可。又需要保证字典序最小,则对于范围 \([L_1, R_1], [L_2, R_2]\),其中有贡献的区间实际仅有:\([L_1, L_1 + 9], [L_2, L_2 + 9]\)

则考虑枚举 \(a_1\in [L_1, L_1 + 9], a_2 \in [L_2, L_2 + 9]\) 然后大力检查即可。

//
/*
By:Luckyblock
*/
#include <bits/stdc++.h>
#define LL long long
//=============================================================
//=============================================================
bool check(int a1_, int a2_, int a3_, int a4_) {
  a1_ %= 10, a2_ %= 10;
  if (a1_ * a2_ % 10 == a3_) {
    if (a4_ != -1) return a2_ * a3_ % 10 == a4_;
    return true;
  }
  return false;
}
//=============================================================
int main() {
  //freopen("1.txt", "r", stdin);
  std::ios::sync_with_stdio(0), std::cin.tie(0);
  int T; std::cin >> T;
  while (T --) {
    int n, L1, R1, L2, R2, ans1 = -1, ans2 = -1; 
    std::cin >> n >> L1 >> R1 >> L2 >> R2;
    std::vector<int> a(n + 2, -1);
    
    for (int i = 3; i <= n; ++ i) std::cin >> a[i];
    for (int a1 = L1; a1 <= std::min(R1, L1 + 10); ++ a1) {
      for (int a2 = L2; a2 <= std::min(R2, L2 + 10); ++ a2) {
        if (check(a1, a2, a[3], a[4])) {
          ans1 = a1, ans2 = a2;
          break;
        }
      }
      if (ans1 != -1) break;
    }
    std::cout << ans1 << " " << ans2 << "\n";
  }
  return 0;
}

D

将原序列变为回文序列,则仅需分别考虑,如何令对称的两个位置相等即可。

\(a_i\) 变为 \(a_i \oplus \left\lfloor \frac{a_i}{2} \right\rfloor\) 实际上是令 \(a_i\) 异或 \(a_i\) 右移一位。然后手玩下发现对于某个数操作不会太多次之后就有循环节了,于是可以大力预处理出所有操作结果以及步数。

具体为什么有循环节?显然上述操作方式下最高位的 1 永远不变,手玩下发现在大概 \(O(\log x)\) 级别次操作后,即可令 \(x\) 变为 01 交替的形式,再操作一次后即可变成全 1,在操作一次后就只剩最高位的 1,然后就有了循环节。到达循环节的步数以及循环节的长度都大概是 \(O(\log x)\) 级别。

然后对于所有对称的两个位置,枚举所有公共的操作结果并求最小步数之和即可。

//
/*
By:Luckyblock
*/
#include <bits/stdc++.h>
#define LL long long
const int kInf = 1e9 + 2077;
//=============================================================
//=============================================================
int solve(int x_, int y_) {
  std::map<int, int> cntx, cnty;
  cntx[x_] = cnty[y_] = 0;
  for (int i = 1, pre = x_, now = -1; ; ++ i, pre = now) {
    now = pre ^ (pre >> 1);
    if (cntx.count(now)) break;
    cntx[now] = i;
  }
  for (int i = 1, pre = y_, now = -1; ; ++ i, pre = now) {
    now = pre ^ (pre >> 1);
    if (cnty.count(now)) break;
    cnty[now] = i;
  }

  int ret = kInf;
  for (auto [key, val]: cntx) {
    if (cnty.count(key)) ret = std::min(ret, val + cnty[key]);
  }
  return ret;
}
//=============================================================
int main() {
  //freopen("1.txt", "r", stdin);
  std::ios::sync_with_stdio(0), std::cin.tie(0);
  int T; std::cin >> T;
  while (T --) {
    int n, ans = 0; std::cin >> n;
    std::vector<int> a(n + 1);
    for (int i = 1; i <= n; ++ i) std::cin >> a[i];
    for (int i = 1, j = n; i < j; ++ i, -- j) {
      int ret = solve(a[i], a[j]);
      if (ret == kInf) {
        ans = -1;
        break;
      }
      ans += ret;
    }
    std::cout << ans << "\n";
  }
  return 0;
}
/*
a   = bn bn-1 ... b3 b2 b1
a/2 = 0  bn   ... b4 b3 b2

0001111000
0000111100
0001000100

0001000100
0000100010
0001100110

0001100110
0000110011
0001010101

0001010101
0000101010
0001111111

0001111111
0000111111
0001000000

0001000000
0000100000
0001100000

0001100000
0000110000
0001010000

0001010000
0000101000
0001111000

0001111000
0000111100
0001000100


101010
010101
111111
*/

E

显然当且仅当 \(\operatorname{sum} \le n\) 时无解。

对于 \(n\le 4 n\) 时,发现令 1 变为 4 代价是 \(\operatorname{sum}-3\),4 变为 8 代价是 \(\operatorname{sum}-4\),则显然此时仅使用 1 和 4 是最好的,于是仅需求最多能有多少 4。

对于 \(n\ge 4 n\) 时,先令所有位置为 4。考虑如何每次令一个数字中洞的数量 \(+1\),容易发现有用的数字仅有 4 和 8。且构造规律为:

  • 若当前所有位置为 \(48\cdots 88\),尝试令所有位置变为 \(88\cdots 88\)
  • 若当前所有位置为 \(88\cdots 88\),尝试令所有位置变为 \(888\cdots 88\)

发现改变的轮数不超过 20 次,于是预处理一下变化时 \(\operatorname{sum}\) 的变化量,然后直接大力模拟即可。

//
/*
By:Luckyblock
*/
#include <bits/stdc++.h>
#define LL long long
//=============================================================
std::map<LL, LL> next;
//=============================================================
void init() {
  next[4] = 4;
  for (LL i = 8, j = 40; i <= 1e10; i += 2 * j, j *= 10ll) {
    next[i] = next[i + j] = j;
  }
}
//=============================================================
int main() {
  //freopen("1.txt", "r", stdin);
  std::ios::sync_with_stdio(0), std::cin.tie(0);

  init();

  int T; std::cin >> T;
  while (T --) {
    int n; LL sum; std::cin >> n >> sum;
    if (sum < n) {
      std::cout << -1 << "\n";
      continue;
    } else if (sum <= 4 * n) {
      sum -= n;
      for (int i = 1; i <= n; ++ i) {
        sum -= 3;
        std::cout << (sum >= 0 ? 4 : 1) << " ";
      }
      std::cout << "\n";
      continue;
    }

    sum -= 4 * n;
    std::vector<LL> ans(n + 1, 4);
    while (sum >= next[ans[n]]) {
      for (int i = 1; i <= n; ++ i) {
        LL c = next[ans[i]];
        if (sum >= c) ans[i] += c, sum -= c;
      }
    }
    for (int i = 1; i <= n; ++ i) std::cout << ans[i] << " ";
    std::cout << "\n";
  }
  return 0;
}
/*
只使用 1 4 8

8 8
48 8
88 8
88 48
88 88
488 488
888 488
888 888

1 1 1 1 1 1 ……

1 4 4 4 4 4 ……

4 4 4 4 4 4 ……

4 4 4 8 8 8 ……

8 8 8 8 8 8 ……

8 8 8 80 88 88 88 …… (80 不超过 1 个)

80 88 88 88 88 ……

880 888 888 888 …… (880 不超过 1 个)


考虑每次加一个洞的代价
*/

F

考虑钦定每个位置 \(i\) 为一段合法子数组的 \(b_1/b_m\)较小一方,然后考虑该位置是左边界 \(b_1\) 还是右边界 \(b_m\)

\(a_i\) 若为左边界 \(b_1\),则需要找到 \(i\) 右侧的所有位置 \(j\),满足 \(\forall i\le k <j, a_j > a_i > a_k\),即 \(a_j\) 是右侧第一个大于 \(a_i\) 的元素,且 \([i, j]\) 中不能有等于 \(a_i\) 的元素,这等价于:找到 \(i\) 右侧第一个大于等于 \(a_i\) 的元素 \(a_j\),然后判断 \(a_j\not= a_i\)

单调栈实现即可,同理可以得到 \(a_i\) 为右边界 \(b_m\) 的贡献。

//
/*
By:Luckyblock
*/
#include <bits/stdc++.h>
#define LL long long
const int kN = 2e5 + 10;
//=============================================================
int n, top, a[kN], st[kN];
int L[kN], R[kN], cntL[kN], cntR[kN];
//=============================================================
//=============================================================
int main() {
  //freopen("1.txt", "r", stdin);
  std::ios::sync_with_stdio(0), std::cin.tie(0);
  int T; std::cin >> T;
  while (T --) {
    std::cin >> n;
    for (int i = 1; i <= n; ++ i) {
      std::cin >> a[i];
      L[i] = 0, R[i] = n + 1;
      cntL[i] = cntR[i] = 0;
    }

    top = 0;
    for (int i = 1; i <= n; ++ i) {
      while (top && a[i] >= a[st[top]]) R[st[top]] = i, -- top, ++ cntR[i];
      st[++ top] = i;
    }
    top = 0;
    for (int i = n; i; -- i) {
      while (top && a[i] >= a[st[top]]) L[st[top]] = i, -- top, ++ cntL[i];
      st[++ top] = i;
    }

    LL ans = 0;
    for (int i = 1; i <= n; ++ i) {
      if (L[i] && a[L[i]] > a[i] && i - L[i] >= 2) ++ ans;
      if (R[i] != n + 1 && a[R[i]] > a[i] && R[i] - i >= 2) ++ ans;
    }
    std::cout << ans << "\n";
  }
  return 0;
}
/*
1
6
5 1 1 1 4 4
5 1 3 4 5 6
*/

写在最后

这么套 b 题做了两个小时,汤碗。

posted @ 2025-08-28 21:51  Luckyblock  阅读(31)  评论(0)    收藏  举报