2024-10-30-ABC377
A - Rearranging ABC
题意
给定三个字母的字符串,问是不是由ABC构成的。
解法
排个序判断一下即可。
#include <bits/stdc++.h>
using namespace std;
int main() {
string s;
cin >> s;
sort(s.begin(), s.end());
if (s == "ABC")
cout << "Yes" << '\n';
else
cout << "No" << '\n';
return 0;
}
B - Avoid Rook Attack
题意
国际象棋,给定一个棋盘,其中#表示车,可以上下左右随便走,问有多少个位置不会被车给覆盖。
解法
用两个数组标记一下哪些行和列被车覆盖了,然后跑一遍每个格子判断一下是否没被行列覆盖。
#include <bits/stdc++.h>
using namespace std;
bool r[8], c[8] ;
int main() {
for (int i = 0; i < 8; i++) {
string S;
cin >> S;
for (int j = 0; j < 8; j++) {
if (S[j] == '#') {
r[i] = true;
c[j] = true;
}
}
}
int ans = 0;
for (int i = 0; i < 8; i++)
for (int j = 0; j < 8; j++)
if (!r[i] && !c[j])
ans++;
cout << ans << "\n";
return 0;
}
C - Avoid Knight Attack
题意
国际象棋,给定一个棋盘,其中#表示马,马走日,问有多少个位置不会被马给覆盖。
解法
用map记录一下被覆盖的点,在N*M里一个一个减就行了
#include <bits/stdc++.h>
using namespace std;
int dx[] = {0, 2, 1, -1, -2, -2, -1, 1, 2};
int dy[] = {0, 1, 2, 2, 1, -1, -2, -2, -1};
//八个方向
int main() {
int N, M;
cin >> N >> M;
map<pair<int,int>,bool> s;
long long ans = 1LL * N * N;
for (int i = 0; i < M; i++) {
int a, b;
cin >> a >> b;
for (int k = 0; k < 9; k++) {
int x = a + dx[k];
int y = b + dy[k];
if (1 <= x && x <= N && 1 <= y && y <= N && !s.count({x, y})) {
//不能越界并且以前没有被覆盖过
ans--;
s[{x, y}]=1;
}
}
}
cout << ans << "\n";
return 0;
}
D - Many Segments 2
题意
给很多对(L,R),请你求在M范围内有多少对(l,r)使其没有包含任何一对(L,R)
2 4
1 2
3 4
5
解法
发现:如果(l,r)是没有包含任何一对(L,R)的,那么(l+1,r)肯定也是满足条件的
那么题目就可以转化成,对于一个固定的r,合法的l的有多少个,也就是求一个离当前r最远的l在哪,然后这之间的一共(r-l+1)个l都是合法的。
我们假设现在已经知道了r-1最远的l在哪,设为l'
那么l'到r想要包含L,R,唯一的可能性就是有L,R的R刚好为r,注意有可能有很多对满足条件的(L,R),那么新的r配对的l应该是最大的L+1,即l=max(l',Lmax+1)
所以我们先计算一下对于每个R最大的L+1在哪里,然后从前往后递推一遍每个r的l即可。
#include <bits/stdc++.h>
using namespace std;
int maxL[200005];
int l[200005];
int main() {
int N, M;
cin >> N >> M;
//计算每一个R最近的L在哪里
for (int i = 0; i < N; i++) {
int L, R;
cin >> L >> R;
maxL[R] = max(maxL[R], L + 1);
}
//计算每一个r最远的合法l在哪里
l[0]=1;//注意初始化
for (int i = 1; i <= M; i++) {
l[i] = max(l[i - 1], maxL[i]);
}
long long ans = 0;
for (int i = 1; i <= M; i++) {
ans += i - l[i] + 1;
}
cout << ans << "\n";
return 0;
}
E - Permute K times 2
题意
给定一个排列pi(排列指其中的数是由1~n构成的),一共进行k此操作,每次操作将pi替换成ppi,问操作之后的排列。
6 3
5 6 3 1 2 4
解法
遇到排列可以立即考虑建图,i->pi(其他建法也可以,比如pi->ppi,通常表示数字的下一个数字是谁),建图后这个排列应该是由若干个环组成。
然后就可以对着样例尝试:
5 6 3 1 2 4
2 4 3 5 6 1 一步
4 5 3 6 1 2 二步
6 1 3 2 4 5 三步
接下来就是找规律,对于每一个位置,我们看它上面数字怎样变化的,
先看一步的情况:
1号位上的数一步后变成了2
2号位上的数一步后变成了4
3号位上的数一步后变成了3
4号位上的数一步后变成了5
5号位上的数一步后变成了6
6号位上的数一步后变成了1
进图看,不难发现,每个位置往后走2步就是他们的数,
再同样观察二步、三步的情况,会发现分别是向后走4步、8步,最后可以得出其实就是走2^k步的结论
所以解法就是把环处理出来,设环的大小为sz,那么每个位置上的数就是用快速幂算出\(2^k mod sz\)之后的数是谁就行。
#include <bits/stdc++.h>
using namespace std;
//快速幂,计算a的b次方模p
int power(int a, long long b, int p) {
int res = 1;
for (; b; b /= 2, a = 1LL * a * a % p) {
if (b & 1) {
res = 1LL * res * a % p;
}
}
return res;
}
int main() {
int N;
long long K;
cin >> N >> K;
vector<int> P(N);
for (int i = 0; i < N; i++) {
cin >> P[i];
P[i]--;
}
vector<bool> vis(N);
for (int i = 0; i < N; i++) {
if (vis[i]) {
continue;
}
int j = i;
vector<int> a;
while (!vis[j]) {
vis[j] = true;
a.push_back(j);
j = P[j];
}
long long d = power(2, K, a.size());
for (int x = 0; x < a.size(); x++) {
P[a[x]] = a[(x + d) % a.size()];
}
}
for (int i = 0; i < N; i++) {
cout << P[i] + 1 << " \n"[i == N - 1];
}
return 0;
}