比赛链接:https://atcoder.jp/contests/abc318
比赛时间:2023年9月2日 20:00-21:40
A题:Full Moon
标签:模拟、数学
题意:给定一个起始
m
m
m和上限
n
n
n,每次增量
p
p
p,求能加几次。
题解:数据比较小,可以直接暴力;数学方法算的话,注意边界。
代码:
#include <bits/stdc++.h>
using namespace std;
int main() {
int n, m, p;
cin >> n >> m >> p;
int ans = 0;
for (int i = m; i <= n; i += p) {
ans++;
}
cout << ans << endl;
return 0;
}
B题:Overlapping sheets
标签:模拟、暴力
题意:给定
n
n
n个矩形的覆盖区域,求最终地图被覆盖的区域面积。
题解:数据比较小,直接二维数组标记对应点就好了,实际标记的时候,可以让$ j<b、k<d$,使得后面算面积的时候 直接计算覆盖的点的范围就好了。
代码:
#include <bits/stdc++.h>
using namespace std;
int s[200][200];
int main() {
int n;
cin >> n;
int a, b, c, d;
for (int i = 1; i <= n; i++) {
cin >> a >> b >> c >> d;
for (int j = a; j < b; j++) {
for (int k = c; k < d; k++) {
s[j][k]++;
}
}
}
int ans = 0;
for (int i = 0; i <= 100; i++) {
for (int j = 0; j <= 100; j++) {
if (s[i][j] > 0) ans++;
}
}
cout << ans << endl;
return 0;
}
C题:Blue Spring
标签:贪心、模拟
题意:
N
N
N天火车旅行计划,每天火车车票为
F
i
F_i
Fi元,可以购买能使用
D
D
D天的乘车劵(不一定要连续天),乘车券每张
P
P
P元。求
N
N
N天火车旅行的最少花费。
题解:贪心策略:
D
D
D天直接购买火车车票的费用超过
P
P
P元,就购买乘车劵。我们可以先按车票费用排个序,然后尽量拿费用大的去试试 能不能通过购买乘车劵降低花费。
代码:
#include <bits/stdc++.h>
using namespace std;
const int N = 2e5 + 10;
typedef long long ll;
ll n, d, p, f[N];
int main() {
cin >> n >> d >> p;
for (int i = 1; i <= n; i++) {
cin >> f[i];
}
sort(f + 1, f + 1 + n);
ll ans = 0, c = 0, sum = 0;
for (int i = n; i >= 1; i--) {
sum += f[i];
c++;
if (c == d) {
ans += min(p, sum);
c = 0;
sum = 0;
}
}
if (c > 0) ans += min(p, sum);
cout << ans << endl;
return 0;
}
D题:General Weighted Max Matching
标签:深度优先搜索
题意:给定一个
N
N
N个顶点的加权无向完全图,给定任意两个顶点之间的边权,求满足 “所选边的端点是成对不同” 条件的最大可能总权值。
题解:暴力深搜下去就好了,一个个点遍历下去,如果当前点没有被选,那就选上试试,然后遍历他能走到的其他顶点(没有被标记过的),然后对应打上标记,记得回溯处理一下。过程中更新一下最大的总权值。
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
ll d[20][20], ans = 0, n;
bool vis[20];
void dfs(ll p, ll sum) {
ans = max(ans, sum);
if (p == n + 1) {
return ;
}
dfs(p + 1, sum);
if (!vis[p]) {
for (int j = p + 1; j <= n; j++) {
if (!vis[j]) {
vis[p] = 1; vis[j] = 1;
dfs(p + 1, sum + d[p][j]);
vis[p] = 0; vis[j] = 0;
}
}
}
}
int main() {
cin >> n;
for (int i = 1; i <= n; i++) {
for (int j = i + 1; j <= n; j++) {
cin >> d[i][j];
}
}
dfs(1, 0);
cout << ans;
return 0;
}
E题:Sandwiches
标签:数学、思维
题意:给你一个长度为
N
N
N 的正整数序列:
A
=
(
A
1
,
A
2
,
…
,
A
N
)
A=(A_1,A_2,…,A_N)
A=(A1,A2,…,AN),求满足下列所有条件的正整数三元组
(
i
,
j
,
k
)
(i,j,k)
(i,j,k) 的个数:
- $ 1≤i<j<k≤N$
- $ A_i=A_k$
- $ A_i \neq A_j$
题解:这道题本质其实就是:求相等的数之间夹的不同的数的个数。举几个例子,我们来找找规律:
设
k
k
k为非
11
11
11的数
11
11
11
a
a
a个
k
k
k
11
11
11 => 符合要求的有
a
a
a个
11
11
11
a
a
a个
k
k
k
11
11
11
b
b
b个
k
k
k
11
11
11 => 符合要求的有
2
a
+
2
b
2a+2b
2a+2b个
11
11
11
a
a
a个
k
k
k
11
11
11
b
b
b个
k
k
k
11
11
11
c
c
c个
k
k
k
11
11
11 => 符合要求的有
3
a
+
4
b
+
3
c
3a+4b+3c
3a+4b+3c个
依次类推如果夹了
a
、
b
、
c
、
d
a、b、c、d
a、b、c、d,=>
4
a
+
6
b
+
6
c
+
4
d
4a+6b+6c+4d
4a+6b+6c+4d个
在图上可以画一画,每个
11
11
11会和对应的其他
11
11
11,组成的区间,然后去统计一下每块相邻区间的计算次数,能得到式子:
k
∗
(
l
e
n
−
k
)
k*(len-k)
k∗(len−k)。
实际的算法流程就先把对应数值的下标塞到一个不定长数组里面,然后求一下相邻的相同的数,每一块区间能够产生的贡献,累加一下就可以了。
代码:
#include <bits/stdc++.h>
using namespace std;
const int N = 3e5 + 10;
typedef long long ll;
vector<ll> a[N];
int main() {
ll n, x, ans = 0;
cin >> n;
for (ll i = 1; i <= n; i++) {
cin >> x;
a[x].push_back(i);
}
for (ll i = 1; i <= n; i++) {
ll len = a[i].size();
for (ll k = 1; k < len; k++) {
ans += k * (len - k) * (a[i][k] - a[i][k-1] - 1);
}
}
cout << ans << endl;
return 0;
}