2024-09-15-ABC371
A - Jiro
题意:给定三个字符,分别是大于号或者小于号,分别表示AB,AC,BC之间的大小关系,问三个数的中间大的数是谁。
解法:分类讨论一下
如果全都是大于号或者小于号,那么中间大的肯定是B。
剩下的情况:>><,<<>是C是第二大,<>>,><<是A第二大,><>,<><不存在。
可以根据六个情况都写个if判断一下,当然也可以找一下规律,如果前两个相同就是C第二大,如果是后两个符号相同就是A第二大,否则就是B第二大。
#include <bits/stdc++.h>
using namespace std;
int main()
{
char a, b, c;
cin >> a >> b >> c;
if (a != b) cout << "A\n";
else if (b != c) cout << "C\n";
else cout << "B\n";
return 0;
}
B - Taro
题意:有n个家庭和m个孩子按顺序出生,每个孩子告诉你ai和bi,分别是他是哪个家庭的以及性别,问对于每一个孩子,他是不是这个家庭的第一个男孩。
样例
2 4
1 M
1 M
2 F
2 M
Yes
No
No
Yes
解法:开一个数组记录一下家庭是否有过男孩即可。
#include <bits/stdc++.h>
using namespace std;
int main() {
int N, M;
cin >> N >> M;
bool has[105];
for (int i = 0; i < M; i++) {
int A;
char B;
cin >> A >> B;
A--;
if (!has[A] && B == 'M') {
has[A] = true;
cout << "Yes\n";
} else {
cout << "No\n";
}
}
return 0;
}
C - Make Isomorphic
题意:给定两张有N个顶点的简单无向图G和H(不存在重边和自环),以及在第二张图上任意两个点之间删除或者添加边的代价aij,问最小的代价,使两张图同构(存在一种点到点的映射,使两张图完全一样,说人话就是不看点的编号,两张图长得是一样的)。
样例
解法:注意到只有八个点,所以我们可以全排列枚举点的映射,然后检查G中的每一条边是否在H中,计算代价即可。
#include <bits/stdc++.h>
using namespace std;
int g[10][10], h[10][10], a[10][10];
int main() {
int n;
cin >> n;
int mg, mh;
cin >> mg;
for (int i = 0; i < mg; i++) {
int x, y;
cin >> x >> y;
g[x][y] = g[y][x] = 1;
}
cin >> mh;
for (int i = 0; i < mh; i++) {
int x, y;
cin >> x >> y;
h[x][y] = h[y][x] = 1;
}
for (int i = 1; i <= n - 1; i++)
for (int j = i + 1; j <= n; j++) {
cin >> a[i][j];
a[j][i] = a[i][j];
}
int p[10], ans = 1e9;
for (int i = 1; i <= n; i++) p[i] = i;
do {
int tmp = 0;
for (int i = 1; i <= n - 1; i++)
for (int j = i + 1; j <= n; j++)
if (g[i][j] != h[p[i]][p[j]]) //g图里的i点就是h图里的p[i]点
tmp += a[p[i]][p[j]];
ans = min(ans, tmp);
} while (next_permutation(p + 1, p + n + 1));
cout << ans;
return 0;
}
D - 1D Country
题意:有一个数轴,在数轴上有n个村落,每个村落给定村落的位置和村落的人数,一共Q次询问,问数轴上L到R之间有多少人。
样例
4
1 3 5 7
1 2 3 4
4
1 1
2 6
0 10
2 2
1
5
10
0
解法:做一下前缀和,然后对于每个询问lowerbound一下L和R在数组里的位置(就是看数轴上的L和R在数组里面最近的村庄在数组里是谁),前缀和减一下就好了。
#include <bits/stdc++.h>
using namespace std;
int X[200005],P[200005];
long long pre[200005];
int main() {
int N;
cin >> N;
for (int i = 0; i < N; i++) {
cin >> X[i];
}
for (int i = 0; i < N; i++) {
cin >> P[i];
}
for (int i = 0; i < N; i++) {
pre[i + 1] = pre[i] + P[i];
}
int Q;
cin >> Q;
for (int i = 0; i < Q; i++) {
int L, R;
cin >> L >> R;
R++;
int l = lower_bound(X, X+N, L) - X;
int r = lower_bound(X, X+N, R) - X;
cout << pre[r] - pre[l] << "\n";
}
return 0;
}
E - I Hate Sigma Problems
题意:给定n个数,定义f(l,r)为数组中下标l到r(包含lr)中不同的数的个数,对f(i,j)求和:i从1到n,j从i到n,
理解一下sigma函数,这个公式的意思可以写成代码表示:
for(int i=1;i<=N;i++)
for(int j=1;j<=n;j++)
ans+=f(i,j);
样例
3
1 2 2
8
解法:我们考虑一个数字在多少个不同的LR区间内贡献了答案,比如数组为2321,那么2这个数字在12区间里贡献了答案,也在13区间里贡献了答案,虽然1到3区间里有两个2,但只加了1的答案,所以我们考虑把每个独特数字的贡献都算给这个区间里最右边的数字的,这样就避免了重复。
现在我们考虑对于每个数数字统计他的贡献。
1 | 2 | 3 | 1 | 4 | 5 | 6 | 1 | 7 | 8 | 9 | 10 | 1 | 11 | 12 |
---|
考虑一个这样的数组里的1的贡献,对于第一个1,他会在区间12,13给出贡献,第二个1会在14,15,16,17,24,25,26,27,34,35,36,37,44,45,46,47给出贡献,我们不难发现,其实就是L可以在这个数前任选,R只能选到下一个相同的数的前一位,我们设这个数的下标为lst,下一个数的下标为i,那么贡献的区间数就是lst*(i-lst)。
那我们的思路就已经有了,每次遇到一个数,我们通过当前数(i)和上一个相同数的下标(lst)计算出上一个数的贡献区间数即可,最后记得还要统一加上最后一个数到n的区间。
#include <bits/stdc++.h>
using namespace std;
int A[200005], lst[200005];//lst[i]表示上一个数字i的下标在哪
int main() {
int N;
cin >> N;
long long ans = 0;
for (int i = 1; i <= N; i++) {
cin >> A[i];
ans += 1LL * (i - lst[A[i]]) * lst[A[i]];
lst[A[i]] = i;
}
for (int i = 1; i <= N; i++) {
ans += 1LL * (N - lst[i] + 1) * lst[i];
}
cout << ans << "\n";
return 0;
}