A - CF540A
签到,不讲。
int main() {
int n;
string a, b;
cin >> n >> a >> b;
int ans = 0;
for (int i = 0; i < n; i++) {
int diff = std::abs(a[i] - b[i]);
ans += std::min(diff, 10 - diff);
}
cout << ans;
return 0;
}
B - CF339B
题目大意
环形的路上有 $n$ 个房子,需要以如下的顺序访问 $$ 1 \to a_1 \to a_2 \to \cdots \to a_n $$ 智能顺时针移动,问需要走几步?
分析
按照移动顺序模拟即可,若 $a_i \geqslant now$ 则正常走,若 $a_i < now$ 则需要绕一圈。
int main() {
int n, m;
cin >> n >> m;
int now = 1;
ll ans = 0;
for (int i = 0; i < m; i++) {
int ai;
cin >> ai;
if (ai >= now) {
ans += ai - now;
} else {
ans += ai + n - now;
}
now = ai;
}
cout << ans;
return 0;
}
C - CF779C
题目大意
有 $n$ 个糖果,今天买的价格是 $\{a_i\}$,一周后买的价格是 $\{b_i\}$。今天至少买 $k$ 个,问最少要花多少钱。
分析
糖果今天买的优惠是 $d_i = a_i - b_i$,我们可以以 $d_i$ 排序,选择最值的 $k$ 个。
注意当今天买比一周后便宜时,应该也买上。
using pii = pair<int, int>;
int main() {
int n, k;
cin >> n >> k;
vector<int> a(n), b(n);
for (auto &ai : a) {
cin >> ai;
}
for (auto &bi : b) {
cin >> bi;
}
vector<pii> v(n);
for (int i = 0; i < n; i++) {
v[i] = {a[i] - b[i], i};
}
sort(v.begin(), v.end());
ll ans = 0;
for (int i = 0; i < n; i++) {
int id = v[i].second;
ans += (i < k || v[i].first < 0) ? a[id] : b[id];
}
cout << ans;
return 0;
}
D - CF534C
题目大意
有 $n$ 个骰子,其分别能投出 $1 \sim d_i$ 的数。限制所有骰子朝上数字之和为 $A$,问在所有情况中,每个骰子各有多少数字永不出现。
分析
考虑每个骰子可能出现数字的限制,易得
- 当其余骰子均取到上限 $d_i$ 时,此位置取到下限 $\max\{1, A - \sum d_i + d_i\}$。
- 当其余骰子均取到下限 $1$ 时,此位置取到上限 $\max\{ d_i, A - (n - 1)\}$。
int main() {
ll n, A, sum = 0;
cin >> n >> A;
vector<ll> a(n);
for (auto &ai : a) {
cin >> ai;
sum += ai;
}
for (auto ai : a) {
int mi = std::max(1ll, A - (sum - ai));
int ma = std::min(ai, A - (n - 1));
int ans = ai - (ma - mi + 1);
cout << ans << ' ';
}
return 0;
}
E - CF28B
题目大意
给定两个长为 $n$ 的数组 $\{a_i\}, \{d_i\}$,当 $|i - j | = d_i$ 是,$a_i$ 可以和 $a_j$ 交换。
问能否把数组 $\{a_i\}$ 变为自然顺序。
分析
注意到可交换的位置形成了一个联通块,在联通块内的位置都可以任意交换。
我们可以使用并查集 DSU 维护,最终查询 $a_i$ 和 $i$ 是否在一个联通块即可。
struct DSU {
vector<int> v;
DSU(int n) : v(n) {
std::iota(v.begin(), v.end(), 0);
}
int find(int x) {
return x == v[x] ? v[x] : v[x] = find(v[x]);
}
void merge(int x, int y) {
x = find(x), y = find(y);
v[x] = y;
}
};
int main() {
int n;
cin >> n;
vector<int> a(n + 1), d(n + 1);
for (int i = 1; i <= n; i++) {
cin >> a[i];
}
DSU dsu(n + 1);
for (int i = 1; i <= n; i++) {
cin >> d[i];
int l = i - d[i], r = i + d[i];
if (l >= 1) {
dsu.merge(l, i);
}
if (r <= n) {
dsu.merge(r, i);
}
}
for (int i = 1; i <= n; i++) {
if (dsu.find(a[i]) != dsu.find(i)) {
cout << "NO";
exit(0);
}
}
cout << "YES";
return 0;
}
F - CF540C
题意
给两个字符串 $s, t$,可以对其中的字符进行修改。已知单个字母的变换 $A_i \to B_i$ 的代价为 $W_i$,问能否让两个字符串变得一致,并求最小代价。
思路
首先我们可以用 Floyd 算法求得两个字母之间转换的最小代价。
接下来对每一位 $s_i$ 和 $t_i$ 枚举计算哪个字符 $c$ 能够使得 $s_i \to c$ 和 $t_i \to c$ 的代价最小。如果不存在,说明无解。
const int N = 30;
int f[N][N];
void floyd(int n) {
for (int k = 0; k < n; k++) {
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
f[i][j] = min(f[i][j], f[i][k] + f[k][j]);
}
}
}
}
int main() {
int n;
string s, t;
cin >> s >> t >> n;
if (s.length() != t.length()) {
cout << "-1";
exit(0);
}
memset(f, 0x3f, sizeof(f));
for (int i = 0; i < N; i++) {
f[i][i] = 0;
}
while (n--) {
string s1, s2;
int len;
cin >> s1 >> s2 >> len;
int c1 = s1[0] - 'a', c2 = s2[0] - 'a';
f[c1][c2] = std::min(len, f[c1][c2]);
}
floyd(N);
ll ans = 0;
string res = s;
for (int i = 0; i < s.length(); i++) {
int tmp = 1E9;
char ck = 0;
for (int j = 0; j < N; j++) {
int u = f[s[i] - 'a'][j] + f[t[i] - 'a'][j];
if (u < tmp) {
tmp = u;
ck = j + 'a';
}
}
if (tmp > 1E5) {
cout << "-1";
exit(0);
}
ans += tmp;
res[i] = ck;
}
cout << ans << endl << res;
return 0;
}
G - CF540C
题意
这题英文题面很难理解,大概就是地图里的冰块有两种状态,坚硬态和易碎态,如果人物走到了坚硬态的冰块上,就会让冰块进入易碎态,如果人物走到易碎态的冰块上就会掉到下一层。题目问能否让人物从给定起点出发,在终点摔下去。即如果终点一开始不在易碎态,则要先路过一下终点,再绕一圈回到终点,如果一开始就为易碎态则直接走到终点即可。并且除了终点外,其他处于易碎态的冰块人物都不能踩上去。
思路 | By zyl
这题看懂意思就很简单了,直接BFS,不停的判断走的下一个位置是否为坚硬态,如果是坚硬态就让它变为易碎态,并将位置加入队列,如果遇到终点且终点处于易碎态,则说明有解,直接输出 YES 并结束。如果无解,最后一定会有大部分冰块变为易碎态,人物无处可走,所以在while循环结束后输出 NO 即可。
const int N = 510;
int dirs[4][2] = {{1, 0}, {0, 1}, {-1, 0}, {0, -1}};
char mp[N][N];
int main() {
int n, m;
cin >> n >> m;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
cin >> mp[i][j];
}
}
int stx, sty, edx, edy;
cin >> stx >> sty >> edx >> edy;
queue<pii> q;
q.push({stx, sty});
while (!q.empty()) {
pii tp = q.front();
q.pop();
for (int i = 0; i < 4; i++) {
int nx = tp.first + dirs[i][0], ny = tp.second + dirs[i][1];
if (nx >= 1 && nx <= n && ny >= 1 && ny <= m && mp[nx][ny] == '.') {
q.push({nx, ny});
mp[nx][ny] = 'X';
} else if (nx == edx && ny == edy && mp[nx][ny] == 'X') {
cout << "YES" << endl;
return 0;
}
}
}
cout << "NO" << endl;
return 0;
}
H - CF488E
题意
求长为 $n$ 的排列 $\{a_i\}$,使其前缀积 $\prod a_i \bmod n$ 恰为 $[0, \cdots, n - 1]$ 的排列。
思路
不难发现,$1$ 必须在第一个,$n$ 必须放在最后一个。故
$$ \prod_{i=1}^{n - 1} a_i = (n - 1)! $$ 假设 $n$ 存在最小的质因子 $n = p_1 n_1$,当 $n_1 > 2$ 时则 $(n-1)!$ 中必含有 $p_1, 2p_1$,(当 $n$ 为合数且 $n \ne 4$ 时均满足)故
$$ (n-1)! \equiv 0 \pmod n $$
若 $n$ 为质数,依 Wilson 定理有
$$ (n-1)! \equiv -1 \pmod n $$
最简单的想法就是让前缀积变成 $[1, 2, \cdots, n]$,有神奇的构造
$$ \left\{ 1, \frac{2}{1}, \frac{3}{2}, \frac{4}{3}, \cdots, \frac{n}{n-1} \right\} $$
注意到逆元唯一,故这列数都可以看作 $\frac{1}{x}+1$,因此互异。
int main() {
int n;
cin >> n;
if (n == 4) {
cout << "YES\n1\n3\n2\n4";
exit(0);
}
if (n == 1) {
cout << "YES\n1\n";
exit(0);
}
if (!is_prime(n)) {
cout << "NO";
exit(0);
}
vector<int> ans = {1};
for (int i = 2; i < n; i++) {
ans.push_back(1ll * i * qpow(i - 1, n - 2, n) % n);
}
ans.push_back(n);
cout << "YES" << endl;
for (auto ai : ans) {
cout << ai << endl;
}
return 0;
}