逐月信息学 2024 提高组 #4

\(\color{black}\texttt{A. 转盘锁}\)

题目描述

给定一个四位转盘锁,每个转盘上都有 \(0\)\(9\) 的数字。数字 \(i\) 的下一个数字是 \((i+1)\bmod 10\),上一个数字是 \((i-1)\bmod 10\)。每次你可以将一段连续的区间全部往上翻或往下翻一个数字。现在给定一个初始时的转盘,求最少需要多少次操作才能达到目标状态。

思路

首先,一个转盘锁 \(abcd\rightarrow efgh\),可以看作 \(0000\rightarrow (abcd-efgh)\bmod 10\)。所以 BFS 预处理从 \(0000\) 到其他所有状态的最短路即可。

代码

#include<bits/stdc++.h>
using namespace std;

struct Node {
  vector<int> v;
  int dis;
};

int T, ans;
string s, t;
queue<Node> que;
map<vector<int>, int> dist;

void Record(const vector<int> &v, int dis) {
  if(dist.count(v)) {
    return;
  }
  dist[v] = dis;
  que.push({v, dis});
}

void bfs() {
  Record({0, 0, 0, 0}, 0);
  for(; !que.empty(); ) {
    auto [v, dis] = que.front();
    que.pop();
    for(int i = 0; i < 4; ++i) {
      for(int j = i; j < 4; ++j) {
        for(int op : {-1, 1}) {
          for(int k = i; k <= j; ++k) {
            v[k] = (v[k] + op + 10) % 10;
          }
          Record(v, dis + 1);
          for(int k = i; k <= j; ++k) {
            v[k] = (v[k] - op + 10) % 10;
          }
        }
      }
    }
  }
}

void Solve() {
  cin >> s >> t;
  vector<int> v = {(s[0] - t[0] + 10) % 10, (s[1] - t[1] + 10) % 10, (s[2] - t[2] + 10) % 10, (s[3] - t[3] + 10) % 10};
  cout << dist[v] << "\n";
}

int main() {
  ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
  bfs();
  for(cin >> T; T--; Solve()) {
  }
  return 0;
}

\(\color{black}\texttt{B. 多姿多彩}\)

题目描述

定义两个长度均为 \(N\) 的字符串 \(S,T\) 的多样值 \(f(S,T)\)\(\sum \limits_{i=1}^N [S_i \ne T_i]\)

\(\sum \limits_{i=0}^{N-1} \sum \limits_{j=0}^{N-1} f(S\operatorname{LSH}i,S\operatorname{LSH} j)\),其中 \(S\operatorname{LSH}x\) 表示 \(S\) 循环左移 \(x\) 位的结果。

思路

首先考虑这个问题的反面:即把 \(\ne\) 变为 \(=\)。这时每对相同的字符会对答案造成 \(N\) 的贡献。所以这个的答案为 \(\sum \limits_{i=1}^{26} Ncnt_i^2\),其中 \(cnt_i\) 表示字符 \(i\) 出现次数。

然后跟 \(N^3\) 相减就是最终答案。

代码

#include<bits/stdc++.h>
using namespace std;
using ll = long long;

int n, cnt[26];
ll ans;
string s;

int main() {
  ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
  cin >> n >> s;
  s = ' ' + s;
  for(int i = 1; i <= n; ++i) {
    cnt[s[i] - 'a']++;
  }
  for(int i = 0; i < 26; ++i) {
    ans += 1ll * cnt[i] * cnt[i] * n;
  }
  cout << 1ll * n * n * n - ans;
  return 0;
}

\(\color{black}\texttt{C. 黑白树}\)

题目描述

给定一棵树,一开始每个结点为黑色或白色。你可以执行以下操作任意次:选择一个叶子节点并将其到根节点路径上所有结点反色。问最后树上最多能有多少个黑色结点。

posted @ 2024-07-07 11:53  Yaosicheng124  阅读(18)  评论(0编辑  收藏  举报