2020 ACM ICPC, Asia Shanghai Regional

  |  
solved A B C D E F G H I J K L M
6 / 13 · O · Ø · · O · Ø · · Ø O
  • O:比赛时通过
  • Ø:赛后通过
  • !:比赛时尝试了未通过
  • ·:比赛时未尝试

比赛链接

B. Mine Sweeper II

solved by lllllan. 2:05(+1)

题意 在一个nmn*m的扫雷地图中,‘XX’表示地雷‘..’表示没有地雷,没有地雷的方格上的数值为四周八个方格中含地雷的个数。现给出两个nmn*m扫雷的地图,要求将第二个地图通过不多于nm/2n*m/2的操作次数【每次操作可将‘XX’更改为‘..’,或将’..‘更改为’XX‘】,使得变换后的地图中的权值和与第一个地图相等,输出变换后的地图。

题解 操作次数刚好限制为地图方格个数的一半,然后考虑极端情况,两个地图完全一致,权值和必然相同;两个地图完全相反,权值和也相同。所以只要数出两个地图不同方格的个数,考虑将第二地图变换成和第一地图完全一致还是完全相反。最后特判111*1的地图即可。

AC代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
#include <bits/stdc++.h>
using namespace std;

typedef long long ll;
const int N = 1e3 + 10;

int n, m;
char a[N][N], b[N][N];
int main() {
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; ++i) {
scanf("%s", a[i] + 1);
}
int cnt = 0;
for (int i = 1; i <= n; ++i) {
scanf("%s", b[i] + 1);
for (int j = 1; j <= m; ++j) {
if (a[i][j] != b[i][j]) cnt++;
}
}

int flag = 1;
if (cnt > (n * m / 2)) flag = 0;

if (n == 1 && m == 1) {
printf("%c\n", b[1][1]);
return 0;
}

if (flag) {
for (int i = 1; i <= n; ++i) {
printf("%s\n", a[i] + 1);
}
} else {
for (int i = 1; i <= n; ++i) {
for (int j = 1; j <= m; ++j) {
printf("%c", a[i][j] == '.' ? 'X' : '.');
}
printf("\n");
}
}
return 0;
}

D. Walker

Solved By Sstee1XD. (-)

题意 给你一条坐标从00开始的水平线,再给你两个点各自的横坐标和速度,每个点可以任意移动,问最短的时间让两个点遍历完整条线段。

题解 分类讨论,合并情况后发现可以分成三种情况。

  • 让一个点走完全程
  • 两个点往对方的的方向走(对穿),走到尽头
  • 两个点各自走完自己的线段,在中间相遇。其中每个点又有两种情况,可以先走中间,或者先走到自己这边的尽头。

对于前两种情况可以直接求,第三种情况不好确定相遇点,所以考虑二分时间,对于两个点都选择能让自己多走中间线段的方案,相加判断长度。

AC代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
#include <bits/stdc++.h>

using namespace std;

#define double long double
#define endl "\n"
#define dbg(x...) do { cout << #x << " -> "; err(x); } while (0)
void err() { cout << endl; }
template <class T, class... Ts>
void err(const T& arg, const Ts&... args) {
cout << arg << ' '; err(args...);
}
template <class T> inline void read(T &x) {
int f = 0; x = 0; char ch = getchar();
for (; !isdigit(ch); ch = getchar()) f |= (ch == '-');
for (; isdigit(ch); ch = getchar()) x = x * 10 + ch - '0';
if (f) x = -x;
}

typedef long long ll;
const int inf = 0x3f3f3f3f;
const double eps = 1e-11;

double tot, p1, p2, v1, v2;

bool sgn(double x, double y) {
return fabs(y - x) <= eps;
}

bool check(double t) {
// 前后两种分别是先走中间和先走到尽头的情况
double d1 = max((t - p1 / v1) * v1 / 2, (t - 2 * p1 / v1) * v1);
double d2 = max((t - (tot - p2) / v2) * v2 / 2, (t - 2 * (tot - p2) / v2) * v2);
if (d1 < 0 || d2 < 0) return false;
return d1 + d2 >= p2 - p1;
}

void solve() {
cin >> tot >> p1 >> v1 >> p2 >> v2;
if (p2 < p1) swap(p1, p2), swap(v1, v2);
double ans = inf;

ans = min(ans, (min(p1, tot - p1) + tot) / v1); // only one point
ans = min(ans, (min(p2, tot - p2) + tot) / v2);

ans = min(ans, max((tot - p1) / v1, p2 / v2)); // cross each other

double l = 0, r = inf, mid;
while (!sgn(l, r)) {
mid = (l + r) / 2;
if (check(mid)) r = mid;
else l = mid;
}
ans = min(ans, mid);

cout << ans << endl;
}

int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);cout.tie(nullptr);
cout << fixed << setprecision(20);
int _T = 1;
cin >> _T;
while (_T--) solve();
}

G. Fibonacci

solved by Tryna. 0:16(+2)

题意 定义g(x,y)g(x, y),当xyx ·y为偶数时g(x,y)=1g(x,y) = 1,求$$\sum_{i = 1}^{n}\sum_{j = i + 1}^{n}g(f_i, f_j)$$ff为斐波那契数列

题解 答案为(n1)n(2(n/3)+nMod3)((2(n/3)+nMod31))2\frac{(n - 1) * n - (2 * (n / 3) + n Mod 3) * ((2 * (n / 3) + nMod 3 - 1))}{2}

AC代码
1
2
3
4
5
6
7
8
9
10
11
12
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
ll n;
int main() {
scanf("%lld", &n);
ll ans = (n - 1) * n / 2;
ll cnt = (2 * (n / 3) + n % 3);
ll sum = cnt * (cnt - 1) / 2;
printf("%lld\n", ans - sum);
return 0;
}

I. Sky Garden

solved by Tryna.(-)

题意 给出n个同心圆,每个圆被等分成2m2m份,求每个点到其他点最短距离之和

题解 比赛的时候写到这题没时间了,然后赛后自己花了两个小时写出来了。这题的关键在于求出一个圆上每个点到其他点的最短距离和。想了一下本来想通过找规律来找到这个答案的,后来发现有点难实现,看了一下数据范围,发现可以O(n2)O(n^2)来算出答案。对于一个点,它有两种方案,一种是走两条半径,另外一种是走圆弧。求出一个圆上所有点到其他点的最短距离和后,这个圆上的点要想到外面的大圆上,他至少需要经过RR(大圆的半径) - rr(小圆的半径)的距离,想要距离最短,必须要走小圆的圆弧。可以发现小圆上的一个点到大圆上所有点的距离就是之前算出来的小圆上每个点到其他点的最短距离和加上2m(Rr)2m(R - r),知道了这些接下来随便乱搞一下就出来了,注意要特判m=1m = 1的情况,因为此时圆心是没有交点的。代码写的比较长。

AC代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
#include<bits/stdc++.h>
using namespace std;
#define eps 1e-8
const double pi = acos(-1.0);
double n, m;

int main() {
scanf("%lf %lf", &n, &m);
if(m == 1){
double result = n * (n + 1);
for(int i = 1; i <= n; i++){
for(int j = i + 1; j <= n; j++)
result += 4 * j;
}
printf("%.10f\n", result);
return 0;
}
double ans = 2 * m * (n * (n + 1) / 2);
double c1 = 0, angle = 1 / (2 * m), sum = 0;
for(int i = 1; i <= 2 * m; i++){
angle = 1.0 / (2 * m);
for(int j = i + 1; j <= 2 * m; j++){
if(i == 1){
if(angle - 0.5 > eps)
c1 += min((double)2, 2 * (1 - angle) * pi);
else
c1 += min((double)2, 2 * angle * pi);
angle += 1.0 / (2 * m);
}
else{
if(angle - 0.5 > eps){
sum += min((double)2, (1 - angle) * 2 * pi);
}
else sum += min((double)2, 2 * angle * pi);
angle += 1.0 / (2 * m);
}
}
}
sum += c1;
for(int i = 1; i <= n; i++){
double cnt = i * sum;
for(int j = i + 1; j <= n; j++) cnt += 2 * m * (2 * m * (j - i) + c1 * i);
ans += cnt;
}
printf("%.10f\n", ans);
return 0;
}

L. Traveling in the Grid World

Solved By Sstee1XD. (-)

题意 给你一个n行m列的方格,左上角为(0,0)(0, 0)点。现在要从(0,0)(0, 0)点走到(n,m)(n, m)点,走的规则如下:

  • 若两点之间不存在其他的整点,则能直接走,否则不能走
  • 任意两次路线的斜率应该不同

问你要走的最短距离是多少。

题解 显然,当gcd(n,m)=1gcd(n, m) = 1时,直接输出。其余情况,不会证明,瞎猜一下要转一次得到最优答案。此时我们发现越往下面找答案会越大(三角形两边之和大于第三边),所以找到第一个点后就breakbreak,最后输出最小的答案。

AC代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
#include <bits/stdc++.h>

using namespace std;

#define endl "\n"
#define dbg(x...) do { cout << #x << " -> "; err(x); } while (0)
void err() { cout << endl; }
template <class T, class... Ts>
void err(const T& arg, const Ts&... args) {
cout << arg << ' '; err(args...);
}
template <class T> inline void read(T &x) {
int f = 0; x = 0; char ch = getchar();
for (; !isdigit(ch); ch = getchar()) f |= (ch == '-');
for (; isdigit(ch); ch = getchar()) x = x * 10 + ch - '0';
if (f) x = -x;
}

typedef long long ll;
const int inf = 0x3f3f3f3f;

ll gcd(ll a, ll b) {
return a == 0? b : gcd(b % a, a);
}

double getDis(double x, double y) {
return sqrt(x * x + y * y);
}

ll n, m;
double k;

void solve() {
scanf("%lld %lld", &n, &m);
if (gcd(n, m) == 1) {
printf("%.10f\n", getDis(n, m));
return;
}
k = (double)m / (double)n;
double minn = n + m;
for (ll _x = 0; _x < n; ++_x) {
ll _y = k * _x + 1;
for (; _y <= m; ++_y) {
if (gcd(_x, _y) != 1) continue;
if (n * _y == m * _x) continue;
minn = min(minn, getDis(_x, _y) + getDis(n - _x, m - _y));
break;
}

}
printf("%.10f\n", minn);
}

int main() {
int _T = 1;
scanf("%d", &_T);
while (_T--) solve();
return 0;
}

M. Gitignore

solved by lllllan. 4:36(+1)

题意NN个可折叠的文件和MM个不可折叠的文件,考虑NN个可折叠的文件最后必须显示多少个。【比如a/ba/ba/ca/c可折叠为a/a/,最后只需显示一个文件即可】

题解 先对MM个不可折叠的文件进行前缀标记【比如a/b/c/da/b/c/d文件是不可折叠的,则用mapmap记录下aaa/ba/ba/b/ca/b/c均为不可折叠】。然后再遍历前NN个可折叠的文件,对必须显示的文件进行计数,可以折叠成已有显示的文件进行忽略。

AC代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
#include<bits/stdc++.h>
using namespace std;

int _T, n, m;
map<string, int> mp, vis;

void run () {
string s[100];
mp.clear(), vis.clear();

cin >> n >> m;
for (int i = 1; i <= n; ++i) {
cin >> s[i];
}
for (int i = 1; i <= m; ++i){
string str;
cin >> str;
int len = str.size();
for (int j = 0; j < len; ++j) {
if (str[j] == '/') {
// cout << str.substr(0, j) << endl;
mp[str.substr(0, j)] = 1;
}
}
}

int ans = 0;
for (int i = 1; i <= n; ++i) {
int flag = 1;
int len = s[i].size();
for (int j = 0; j <= len; ++j) {
if (s[i][j] == '/') {
string tem = s[i].substr(0, j);
if (mp[tem]) flag = 1;
else {
if (vis[tem]) flag = 0;
vis[tem] = 1;
break;
}
}
}
ans += flag;
}
cout << ans << endl;
}

int main () {
cin >> _T;
while (_T--) run();
return 0;
}
文章目录
  1. B. Mine Sweeper II
  2. D. Walker
  3. G. Fibonacci
  4. I. Sky Garden
  5. L. Traveling in the Grid World
  6. M. Gitignore
|