# AtCoder Beginner Contest 182 题解

# Problem A - twiblr (opens new window)

直接输出2A+100B2A+100-B即可,时间复杂度O(1)\mathcal{O}(1)

参考代码 (Python 3)
a, b = map(int, input().split())
print(2 * a + 100 - b)
1
2

# Problem B - Almost GCD (opens new window)

暴力穷举。时间复杂度O(Nmax(a))\mathcal{O}(N\cdot\max(a))

参考代码 (Python 3)
n = int(input())
a = list(map(int, input().split()))
hi = 0
ans = 0
for i in range(2, max(a) + 1):
    cnt = 0
    for j in a:
        if j % i == 0:
            cnt += 1
    if cnt > hi:
        hi = cnt
        ans = i
print(ans)
1
2
3
4
5
6
7
8
9
10
11
12
13

# Problem C - To 3 (opens new window)

利用一个数能被33整除当且仅当其各位之和能被33整除。

  • 如果本身能被33整除,则不需要删除。
  • 如果被33除余11,则首先看是否能删去1111,然后看是否能删去2222
  • 如果被33除余11,则首先看是否能删去1122,然后看是否能删去2211

时间复杂度O(logN)\mathcal{O}(\log N)

参考代码 (Python 3)
s = input()
n = int(s)
if n % 3 == 0:
    print(0)
else:
    a = list(map(int, list(s)))
    c = [0] * 3
    for i in a:
        c[i % 3] += 1
    if c[n % 3] >= 1 and len(a) > 1:
        print(1)
    elif c[3 - n % 3] >= 2 and len(a) > 2:
        print(2)
    else:
        print(-1)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# Problem D - Wandering (opens new window)

记录最远位置ansans,当前位置pospos,前缀和sumsum,以及前缀和的最大值hihi

在每一轮中,首先更新前缀和,然后更新前缀和的最大值,本轮能达到的最大值显然是pos+hipos+hi,用其更新ansans,再用pos+sumpos+sum更新pospos

时间复杂度O(N)\mathcal{O}(N)

参考代码 (C++)
#include <iostream>
#include <vector>

using namespace std;
typedef long long ll;

int main() {
  int n;
  cin >> n;
  int a;
  ll ans = 0, hi = 0, sum = 0, pos = 0;
  for (int i = 1; i <= n; ++i) {
    cin >> a;
    sum += a;
    hi = max(hi, sum);
    ans = max(ans, pos + hi);
    pos += sum;
  }
  cout << ans;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

# Problem E - Akari (opens new window)

将所有灯和墙都放到矩形中,然后逐行从左到右扫描一遍,再从右到左扫描一遍;逐列从上到下扫描一遍,再从下到上扫描一遍。最后统计亮着的格子即可。

时间复杂度O(HW)\mathcal{O}(HW)

参考代码 (C++)
#include <iostream>
#include <vector>

using namespace std;

int main() {
  int h, w, n, m;
  cin >> h >> w >> n >> m;
  int a, b, c, d;
  vector<vector<int>> mat(h, vector<int>(w));
  for (int i = 0; i < n; ++i) {
    cin >> a >> b;
    mat[a - 1][b - 1] = 1;
  }
  for (int i = 0; i < m; ++i) {
    cin >> c >> d;
    mat[c - 1][d - 1] = -1;
  }
  for (int i = 0; i < h; ++i) {
    bool light = false;
    for (int j = 0; j < w; ++j) {
      if (mat[i][j] == 1) {
        light = true;
      } else if (mat[i][j] == -1) {
        light = false;
      } else if (light)
        mat[i][j] = 2;
    }
    light = false;
    for (int j = w - 1; j >= 0; --j) {
      if (mat[i][j] == 1) {
        light = true;
      } else if (mat[i][j] == -1) {
        light = false;
      } else if (light)
        mat[i][j] = 2;
    }
  }
  for (int j = 0; j < w; ++j) {
    bool light = false;
    for (int i = 0; i < h; ++i) {
      if (mat[i][j] == 1) {
        light = true;
      } else if (mat[i][j] == -1) {
        light = false;
      } else if (light)
        mat[i][j] = 2;
    }
    light = false;
    for (int i = h - 1; i >= 0; --i) {
      if (mat[i][j] == 1) {
        light = true;
      } else if (mat[i][j] == -1) {
        light = false;
      } else if (light)
        mat[i][j] = 2;
    }
  }
  int ans = 0;
  for (int i = 0; i < h; ++i)
    for (int j = 0; j < w; ++j)
      ans += mat[i][j] > 0;
  cout << ans;
}
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

# Problem F - Valid payments (opens new window)

我们实际上就是要求出满足

kiai=x\sum k_ia_i=x

并且满足

ki,kiai<ai+1\forall k_i, |k_ia_i| < a_{i+1}

的整数元组{ki}\{k_i\}的种数。

我们不妨从小到大进行选择。容易看到,我们其实只需要记录当前每一个可能达到的总数以及对应的方法数,而不需要记录对应的具体方案。因为ai+1a_{i+1}总是aia_i的倍数,所以在选择完aia_i的系数kik_i后,我们需要保证此时的总数能够被ai+1a_{i+1}整除。同时,因为kiai<ai+1|k_ia_i| < a_{i+1}的限制,因此,对于每一个原有的状态,我们实际上只能有两种选择。

我们以{x,1}\{x,1\}作为初始状态开始递推。看起来,状态数会以指数规模增长,但实际上,任意时刻,我们最多同时保留两个状态,因此总时间复杂度为O(N)\mathcal{O}(N)

参考代码 (C++)
#include <iostream>
#include <unordered_map>
#include <vector>

using namespace std;
typedef long long ll;

int main() {
  int n;
  ll x;
  cin >> n >> x;
  vector<ll> a(n);
  for (int i = 0; i < n; ++i)
    cin >> a[i];
  unordered_map<ll, ll> v;
  v[x] = 1;
  ll ans = 0;
  for (int i = 0; i < n; ++i) {
    unordered_map<ll, ll> nv;
    for (auto [c, f] : v) {
      if (i + 1 < n) {
        ll rem = c % a[i + 1];
        nv[c - rem] += f;
        if (rem > 0)
          nv[c + a[i + 1] - rem] += f;
      } else {
        if (c % a[i] == 0)
          nv[0] += f;
      }
    }
    v = move(nv);
  }
  cout << v[0];
}
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