USACO

Chapter 1

1.4 Wormholes

  • 題意:有 12 個蟲洞,要配對成兩兩一組,一旦碰到蟲洞就會被傳送到另一端,且一直往 +X 方向移動,問有多少種組合能形成環?
  • 這題是很好的題目,能練習實作暴搜。首先要會 DFS 枚舉組合,方法是每次找到第一個沒被用到的點,然後枚舉能和他匹配的點。之後再檢查每個組合是否有環,採取 BFS,每次走兩步,先一步蟲洞,再走到下一個點。
 1/*
 2ID: whitech1
 3TASK: wormhole
 4LANG: C++14               
 5*/
 6#include <bits/stdc++.h>
 7using namespace std;
 8typedef pair<int, int> pii;
 9#define x first
10#define y second
11int n, ans = 0;
12vector<pii> p;
13vector<vector<bool> > G;
14vector<int> pr;
15bool check() {
16    vector<int> visit(n);
17    queue<int> q;
18    for (int i = 0; i < n; i++) q.push(i);
19    while (!q.empty()) {
20        int u = q.front(); q.pop();
21        if (visit[u] > 2 * n) return true;
22        for (int i = 0; i < n; i++) {
23            if (G[pr[u]][i])
24                q.push(i), visit[i] = visit[u] + 2;
25        }
26    }
27    return false;
28}
29void per(int cnt) {
30    if (cnt == n) {
31        if (check()) ans++;
32        return;
33    }
34    for (int i = 0; i < n; i++) {
35        if (pr[i] != -1) continue;
36        for (int j = i + 1; j < n; j++) {
37            if (pr[j] != -1) continue;
38            pr[i] = j, pr[j] = i;
39            per(cnt + 2);
40            pr[i] = pr[j] = -1;
41        }
42        break;
43    }
44}
45int main() {
46    freopen("wormhole.in", "r", stdin);
47    freopen("wormhole.out", "w", stdout);
48    cin >> n;
49    p.resize(n), pr.resize(n, -1);
50    G.resize(n, vector<bool>(n));
51    for (int i = 0; i < n; i++)
52        cin >> p[i].x >> p[i].y;
53    sort(p.begin(), p.end());
54    for (int i = 0; i < n; i++)
55        for (int j = i + 1; j < n; j++)
56            if (p[i].y == p[j].y) {
57                G[i][j] = true;
58                break;
59            }
60    per(0);
61    cout << ans << '\n';
62    return 0;
63}

1.4 Ski Course Design

 1/*
 2ID: whitech1
 3TASK: skidesign
 4LANG: C++14               
 5*/
 6#include <bits/stdc++.h>
 7using namespace std;
 8inline int d(int x) {return x * x;}
 9int main() {
10    freopen("skidesign.in", "r", stdin);
11    freopen("skidesign.out", "w", stdout);
12    int n; cin >> n;
13    vector<int> v(n);
14    for (int i = 0; i < n; i++) cin >> v[i];
15    sort(v.begin(), v.end());
16    int ans = n * d(100);
17    for (int m = 0; m <= 100; m++) {
18        int cnt = 0;
19        for (int a : v)
20            if (a < m) cnt += d(m - a);
21            else if (a > m + 17) cnt += d(a - m - 17);
22        ans = min(ans, cnt);
23    }
24    cout << ans << '\n';
25    return 0;
26}

1.5 Arithmetic Progressions

  • 題意:枚舉 p * p + q * q (0 ≤ p, q ≤ m ≤ 250)序列中的長度為 n ≤ 25 的序列。時間為 5 秒。
  • 作法:把序列先用 bool seq[2 * m * m + 1] 紀錄。枚舉前兩個元素來檢查序列。
 1/*
 2ID: whitech1
 3TASK: ariprog
 4LANG: C++14               
 5*/
 6#include <bits/stdc++.h>
 7using namespace std;
 8typedef pair<int, int> pii;
 9set<int> s;
10int main() {
11    freopen("ariprog.in", "r", stdin);
12    freopen("ariprog.out", "w", stdout);
13    int n, m; cin >> n >> m;
14    vector<int> sq(m + 1);
15    for (int i = 1; i <= m; i++) sq[i] = i * i;
16    for (int i = 0; i <= m; i++)
17        for (int j = i; j <= m; j++)
18            s.insert(sq[i] + sq[j]);
19    set<pii> ans;
20    vector<int> v(s.begin(), s.end());
21    vector<bool> seq(2 * m * m + 1);
22    for (int i : v) seq[i] = true;
23    for (int i : v) {
24        for (int j : v) {
25            if (i >= j) continue;
26            int d = j - i;
27            bool success = true;
28            for (int k = i + (n - 1) * d; k > j; k -= d)
29                if (k >= seq.size() || !seq[k]) {
30                    success = false;
31                    break;
32                }
33            if (success) ans.insert(pii(d, i));
34        }
35    }
36    for (auto i : ans) cout << i.second << " " << i.first << "\n";
37    if (!ans.size()) cout << "NONE\n";
38    return 0;
39}

1.5 Mother’s Milk

  • 題意:給定 1 ≤ A, B, C ≤ 20,三個桶子,問 A 桶為空時,C 有哪些可能的容量。
 1/*
 2ID: whitech1
 3TASK: milk3
 4LANG: C++14               
 5*/
 6#include <bits/stdc++.h>
 7using namespace std;
 8int a, b, c;
 9bool ans[25][25][25];
10void solve(int x, int y, int z) {
11    if (ans[x][y][z]) return;
12    ans[x][y][z] = true;
13    int d;
14    // A -> B
15    d = min(x, b - y);
16    solve(x - d, y + d, z);
17    // A -> C
18    d = min(x, c - z);
19    solve(x - d, y, z + d);
20    // B -> A
21    d = min(a - x, y);
22    solve(x + d, y - d, z);
23    // B -> C
24    d = min(y, c - z);
25    solve(x, y - d, z + d);
26    // C -> A
27    d = min(a - x, z);
28    solve(x + d, y, z - d);
29    // C -> B
30    d = min(b - y, z);
31    solve(x, y + d, z - d);
32}
33int main() {
34    freopen("milk3.in", "r", stdin);
35    freopen("milk3.out", "w", stdout);
36    cin >> a >> b >> c;
37    solve(0, 0, c);
38    for (int i = 0; i < c; i++)
39        if (c - i <= b && ans[0][c - i][i])
40            cout << i << ' ';
41    cout << c << '\n';
42    return 0;
43}

1.6 Binary Numbers

Value Binary Sample Meaning
x 00101100 the original x value
x & -x 00000100 extract lowest bit set
`x -x` 11111100
x ^ -x 11111000 create mask bits to left of lowest bit set
x & (x - 1) 00101000 strip off lowest bit set
`x (x - 1)` 00101111
x ^ (x - 1) 00000111 create mask for lowest-set-bit & bits to its right
~x & (x - 1) 00000011 create mask for bits to right of lowest bit set
`x (x + 1)` 00101101
x / (x & -x) 00001011 shift number right so lowest set bit is at bit 0

1.6 Number Triangles

 1/*
 2ID: whitech1
 3TASK: numtri
 4LANG: C++14               
 5*/
 6#include <bits/stdc++.h>
 7using namespace std;
 8int main() {
 9    freopen("numtri.in", "r", stdin);
10    freopen("numtri.out", "w", stdout);
11    int r; cin >> r;
12    vector<vector<int> > a(r);
13    for (int i = 0; i < r; i++) {
14        a[i].resize(i + 1);
15        for (int j = 0; j <= i; j++)
16            cin >> a[i][j];
17    }
18    for (int i = 1; i < r; i++)
19        for (int j = 0; j <= i; j++)
20            a[i][j] += max(a[i - 1][max(0, j - 1)],
21                           a[i - 1][min(i - 1, j)]);
22    int ans = 0;
23    for (int j = 0; j < r; j++)
24        ans = max(ans, a[r - 1][j]);
25    cout << ans << '\n';
26    return 0;
27}

1.6 Prime Palindromes

  • 題意:找 [a, b] 中同時為迴文和質數的數。
  • 方法:枚舉迴文,再檢查使否為質數,相反會超時。可以發現除了 11 以外的偶位數迴文都會被 11 整除,即非質數。
 1/*
 2ID: whitech1
 3TASK: pprime
 4LANG: C++14               
 5*/
 6#include <bits/stdc++.h>
 7using namespace std;
 8vector<int> prime;
 9bool isprime[10000];
10void gen_prime() {
11    prime.push_back(2);
12    for (int i = 3; i < 10000; i += 2) {
13        if (isprime[i]) continue;
14        prime.push_back(i);
15        for (int j = i * i; j < 10000; j += 2 * i)
16            isprime[j] = true;
17    }
18}
19bool check_prime(int x) {
20    for (int p : prime) {
21        if (p * p > x) return true;
22        if (x % p == 0) return false;
23    }
24    return true;
25}
26int num(int x) {
27    int ret = x;
28    vector<int> v;
29    while (x) {
30        v.push_back(x % 10);
31        x /= 10;
32    }
33    for (int i = 1; i < v.size(); i++)
34        ret = ret * 10 + v[i];
35    return ret;
36}
37int main() {
38    freopen("pprime.in", "r", stdin);
39    freopen("pprime.out", "w", stdout);
40    int a, b; cin >> a >> b;
41    gen_prime();
42    vector<int> ans;
43    int c[] = {2, 3, 5, 7, 11};
44    for (int i : c)
45        if (a <= i && i <= b)
46            ans.push_back(i);
47    for (int i = 10; i < 10000; i++) {
48        int x = num(i);
49        if (a <= x && x <= b && check_prime(x))
50            ans.push_back(x);
51    }
52    for (int i : ans) cout << i << "\n";
53    return 0;
54}

1.6 Superprime Rib

 1/*
 2ID: whitech1
 3TASK: sprime
 4LANG: C++14               
 5*/
 6#include <bits/stdc++.h>
 7using namespace std;
 8vector<int> prime;
 9bool isprime[10000];
10void gen_prime() {
11    prime.push_back(2);
12    for (int i = 3; i < 10000; i += 2) {
13        if (isprime[i]) continue;
14        prime.push_back(i);
15        for (int j = i * i; j < 10000; j += 2 * i)
16            isprime[j] = true;
17    }
18}
19bool check_prime(int x) {
20    if (x < 2) return false;
21    for (int p : prime) {
22        if (p * p > x) return true;
23        if (x % p == 0) return false;
24    }
25    return true;
26}
27vector<int> ans;
28void solve(int cnt, int n, int num) {
29    if (cnt == n) {
30        ans.push_back(num);
31        return;
32    }
33    for (int i = 1; i <= 9; i++) {
34        int d = num * 10 + i;
35        if (check_prime(d)) solve(cnt + 1, n, d);
36    }
37}
38int main() {
39    freopen("sprime.in", "r", stdin);
40    freopen("sprime.out", "w", stdout);
41    int n; cin >> n;
42    gen_prime();
43    solve(0, n, 0);
44    for (int i : ans) cout << i << '\n';
45    return 0;
46}

Chapter 2

2.1 The Castle

  • “Farthest to the west” 是指「最西邊」。
  • 題意:給定每個房間的四道牆,問拆一道牆能形成的最大房間大小。
  • 解法:Flood Fill 算出每個房間的大小,再試著拆每道牆。
 1/*
 2ID: whitech1
 3TASK: castle
 4LANG: C++14               
 5*/
 6#include <bits/stdc++.h>
 7using namespace std;
 8const char D[5] = "WNES";
 9const int dx[4] = {0, -1, 0, 1};
10const int dy[4] = {-1, 0, 1, 0};
11int main() {
12    freopen("castle.in", "r", stdin);
13    freopen("castle.out", "w", stdout);
14    int n, m; cin >> m >> n;
15    vector<vector<int> > w(n, vector<int>(m));
16    for (int i = 0; i < n; i++)
17        for (int j = 0; j < m; j++)
18            cin >> w[i][j];
19    vector<vector<int> > G(n, vector<int>(m));
20    vector<int> sz(1);
21    int mx = 0;
22    for (int i = 0; i < n; i++) {
23        for (int j = 0; j < m; j++) {
24            if (G[i][j]) continue;
25            queue<int> qx, qy;
26            qx.push(i), qy.push(j);
27            G[i][j] = sz.size();
28            sz.push_back(1);
29            while (!qx.empty()) {
30                int x = qx.front(); qx.pop();
31                int y = qy.front(); qy.pop();
32                for (int d = 0; d < 4; d++) {
33                    if ((1 << d) & w[x][y]) continue;
34                    int xx = x + dx[d], yy = y + dy[d];
35                    if (G[xx][yy]) continue;
36                    G[xx][yy] = G[x][y];
37                    qx.push(xx), qy.push(yy);
38                    sz.back()++;
39                }
40            }
41            mx = max(mx, sz.back());
42        }
43    }
44    cout << sz.size() - 1 << "\n" << mx << "\n";
45    mx = 0;
46    int ax, ay; char dir;
47    for (int j = 0; j < m; j++) {
48        for (int i = n - 1; i >= 0; i--) {
49            for (int d = 1; d <= 2; d++) {
50                if ((1 << d) & w[i][j]) {
51                    int ii = i + dx[d], jj = j + dy[d];
52                    if (ii < 0 || jj >= m) continue;
53                    if (G[ii][jj] == G[i][j]) continue;
54                    if (sz[G[i][j]] + sz[G[ii][jj]] > mx) {
55                        mx = sz[G[i][j]] + sz[G[ii][jj]];
56                        ax = i, ay = j, dir = D[d];
57                    }
58                }
59            }
60        }
61    }
62    cout << mx << "\n" << ax + 1 << " " << ay + 1 << " " << dir << "\n";
63    return 0;
64}

2.1 Ordered Fractions

  • 題意:枚舉出 0 ~ 1 之間,分母最大為 N ≤ 160 的分數,依照大小印出。
  • 解法:只將分子與分母互質的最簡分數(reduced fraction)放入,再定義大小排序。
 1/*
 2ID: whitech1
 3TASK: frac1
 4LANG: C++14               
 5*/
 6#include <bits/stdc++.h>
 7using namespace std;
 8typedef pair<int, int> frac;
 9#define x first
10#define y second
11bool cmp (const frac &a, const frac &b) {
12    return a.x * b.y < a.y * b.x;
13}
14int main() {
15    freopen("frac1.in", "r", stdin);
16    freopen("frac1.out", "w", stdout);
17    int n; cin >> n;
18    vector<frac> v;
19    v.push_back(frac(0, 1));
20    for (int i = 1; i <= n; i++)
21        for (int j = 1; j <= i; j++)
22            if (__gcd(i, j) == 1)
23                v.push_back(frac(j, i));
24    sort(v.begin(), v.end(), cmp);
25    for (frac f : v) cout << f.x << '/' << f.y << '\n';
26    return 0;
27}

2.1 Sorting a Three-Valued Sequence

  • 題意:給定一個長度為 n ≤ 1000 的序列,元素只有 1, 2, 3,操作為將任意兩個位置的元素交換,問最少要幾個操作才能將序列排序?
  • 解法:Greedy,先將陣列排序,和原陣列比較。若兩兩交換能到對的位置則交換,各消耗 1 次操作。其餘會形成 cycle,cycle 需要 2 次操作。
 1/*
 2ID: whitech1
 3TASK: sort3
 4LANG: C++14               
 5*/
 6#include <bits/stdc++.h>
 7using namespace std;
 8int main() {
 9    freopen("sort3.in", "r", stdin);
10    freopen("sort3.out", "w", stdout);
11    int n; cin >> n;
12    vector<int> a(n);
13    for (int i = 0; i < n; i++) cin >> a[i];
14    vector<int> b(a.begin(), a.end());
15    sort(b.begin(), b.end());
16    int c[4][4] = {};
17    for (int i = 0; i < n; i++)
18        if (a[i] != b[i]) c[a[i]][b[i]]++;
19    int ans = 0;
20    for (int i = 1; i < 3; i++) {
21        for (int j = i + 1; j <= 3; j++) {
22            int d = min(c[i][j], c[j][i]);
23            ans += d, c[i][j] -= d, c[j][i] -= d;
24        }
25    }
26    int tmp = 0;
27    for (int i = 1; i <= 3; i++)
28        for (int j = 1; j <= 3; j++)
29            tmp += c[i][j];
30    cout << ans + tmp * 2 / 3 << '\n';
31    return 0;
32}

2.1 Healthy Holsteins

  • 題意:牛需要 V 種維他命。有 G 種飼料可選擇,問最少要選擇幾種飼料,才能使維他命的和達到牛所需的量。
  • 解法:枚舉所有組合檢查。
  • 複雜度:$$O(VG2^G)$$
 1/*
 2ID: whitech1
 3TASK: holstein
 4LANG: C++14               
 5*/
 6#include <bits/stdc++.h>
 7using namespace std;
 8int main() {
 9    freopen("holstein.in", "r", stdin);
10    freopen("holstein.out", "w", stdout);
11    int v; cin >> v;
12    vector<int> need(v);
13    for (int i = 0; i < v; i++) cin >> need[i];
14    int n; cin >> n;
15    vector<vector<int> > vit(n, vector<int>(v));
16    for (int i = 0; i < n; i++)
17        for (int j = 0; j < v; j++)
18            cin >> vit[i][j];
19    int N = 1 << n;
20    vector<vector<int> > dp(N, vector<int>(v));
21    int ans = n, id = N - 1;
22    for (int i = 0; i < N; i++) {
23        int cnt = 0;
24        for (int j = 0; j < n; j++) {
25            if ((1 << j) & i) cnt++;
26            else for (int r = 0, k = i | (1 << j); !dp[k][r] && r < v; r++)
27                dp[k][r] = dp[i][r] + vit[j][r];
28        }
29        bool success = true;
30        for (int r = 0; r < v; r++)
31            if (dp[i][r] < need[r]) success = false;
32        if (success) {
33            if (cnt < ans) ans = cnt, id = i;
34            else if (cnt <= ans) id = min(i, id);
35        }
36    }
37    cout << ans;
38    for (int i = 0; i < n; i++)
39        if (id & (1 << i)) cout << ' ' << i + 1;
40    cout << '\n';
41    return 0;
42}

2.1 Hamming Codes

  • 題意:輸出一個大小為 N ≤ 64 的集合,裡面的元素長度為 B ≤ 8 個 bits,兩兩 Hamming distance 不小於 D ≤ 7。
  • 解法:測資弱,直接暴力枚舉。用 __builtin_popcount(x) 算幾個 1-bit。
 1/*
 2ID: whitech1
 3TASK: hamming
 4LANG: C++14               
 5*/
 6#include <bits/stdc++.h>
 7using namespace std;
 8typedef unsigned int ui;
 9vector<ui> ans;
10bool success = false;
11int N, M, D, B;
12void dfs() {
13    if (success) return;
14    if (ans.size() == N) {
15        success = true;
16        sort(ans.begin(), ans.end());
17        for (int i = 0; i < N; i++)
18            cout << ans[i] << " \n"[i % 10 == 9 || i == N - 1];
19        return;
20    }
21    for (ui i = 0; i < M && !success; i++) {
22        bool add = true;
23        for (ui x : ans) {
24            if (__builtin_popcount(i ^ x) < D) {
25                add = false; break;
26            }
27        }
28        if (add) {
29            ans.push_back(i);
30            dfs();
31            ans.pop_back();
32        }
33    }
34}
35int main() {
36    freopen("hamming.in", "r", stdin);
37    freopen("hamming.out", "w", stdout);
38    int B; cin >> N >> B >> D;
39    M = 1 << B;
40    dfs();
41    return 0;
42}

2.2 Preface Numbering

  • 題意:給定 N ≤ 3500,計算 1 ~ N 的羅馬字母表示法共用了每種字母個幾個。
 1/*
 2ID: whitech1
 3TASK: preface
 4LANG: C++14               
 5*/
 6#include <bits/stdc++.h>
 7using namespace std;
 8int d[13] = {1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1};
 9string s[13] = {"M", "CM", "D", "CD", "C", "XC", "L", "XL", "X", "IX", "V", "IV", "I"};
10int cnt[13];
11void solve(int x) {
12    for (int i = 0; i < 13; i++) {
13        cnt[i] += x / d[i];
14        x %= d[i];
15    }
16}
17int main() {
18    freopen("preface.in", "r", stdin);
19    freopen("preface.out", "w", stdout);
20    int n; cin >> n;
21    for (int i = 1; i <= n; i++) solve(i);
22    int ans[7] = {};
23    char c[8] = "IVXLCDM";
24    for (int i = 0; i < 13; i++)
25        for (int j = 0; j < 7; j++)
26            for (char ch : s[i])
27                if (ch == c[j]) ans[j] += cnt[i];
28    for (int i = 0; i < 7; i++)
29        if (ans[i]) cout << c[i] << ' ' << ans[i] << '\n';
30    return 0;
31}

2.2 Subset Sums

  • 題意:給定 N ≤ 39,計算 1 ~ N 有幾種分法能使兩堆一樣多。
 1/*
 2ID: whitech1
 3TASK: subset
 4LANG: C++14               
 5*/
 6#include <bits/stdc++.h>
 7using namespace std;
 8inline int sum(int x) {return x * (x + 1) >> 1;}
 9int main() {
10    freopen("subset.in", "r", stdin);
11    freopen("subset.out", "w", stdout);
12    int n; cin >> n;
13    int m = sum(n);
14    if (m & 1) {
15        cout << "0\n";
16        return 0;
17    }
18    m >>= 1;
19    vector<long long> dp(m + 1);
20    dp[0] = 1;
21    for (int i = 1; i <= n; i++)
22        for (int j = min(sum(i), m); j >= i; j--)
23            dp[j] += dp[j - i];
24    cout << (dp[m] >> 1) << '\n';
25    return 0;
26}

2.2 Runaround Numbers

  • 題意:給定 M ≤ 2^32,找出一個大於 M 的循環數。循環數的定義為,沒用到 0,且每個位元不重複,從第一個位元 d 開始向右走 d 位,走到最右邊則從最左邊繼續,然後在從此位繼續,走完全部回到第一位且不重複。
  • 解法:DFS 暴搜所有解,不斷更新最小值剪枝。
 1/*
 2ID: whitech1
 3TASK: runround
 4LANG: C++14               
 5*/
 6#include <bits/stdc++.h>
 7using namespace std;
 8typedef long long ll;
 9ll m;
10vector<int> v;
11bool used[10];
12bool check(ll num, int len) {
13    if (num <= m) return false;
14    vector<int> cnt(len);
15    for (int i = 0, r = 0; i < len; i++) {
16        r = (r + v[r]) % len;
17        cnt[r]++;
18    }
19    for (int i = 0; i < len; i++)
20        if (!cnt[i]) return false;
21    return true;
22}
23ll ans = 987654321LL;
24void solve(ll now, int len) {
25    if (check(now, len)) ans = min(ans, now);
26    if (now >= ans) return;
27    for (int i = 1; i <= 9; i++) {
28        if (used[i]) continue;
29        used[i] = true, v.push_back(i);
30        solve(now * 10 + i, len + 1);
31        used[i] = false, v.pop_back();
32    }
33}
34int main() {
35    freopen("runround.in", "r", stdin);
36    freopen("runround.out", "w", stdout);
37    cin >> m;
38    solve(0, 0);
39    cout << ans << "\n";
40    return 0;
41}

2.2 Party Lamps

  • 題意:給定 N ≤ 100 和 C ≤ 10000。一開始有 N 盞燈,及四種按鈕,分別為所有燈、奇數燈、偶數燈、3K + 1 狀態改變。以及給定某些燈的狀態。C 為按下按鈕的總次數,求輸出所有最後可能的狀態。
  • 解法:只需要考慮四種按鈕分別按下的奇偶性,以及和 C 的大小及奇偶是否一致。然後枚舉 16 種狀態去花 O(N) 確認。
 1/*
 2ID: whitech1
 3TASK: lamps
 4LANG: C++14               
 5*/
 6#include <bits/stdc++.h>
 7using namespace std;
 8void input(vector<int> &v) {
 9    int x;
10    while (cin >> x && x != -1) v.push_back(x);
11}
12int main() {
13    freopen("lamps.in", "r", stdin);
14    freopen("lamps.out", "w", stdout);
15    int n, c; cin >> n >> c;
16    vector<int> on, off;
17    input(on); input(off);
18    int start[4] = {1, 1, 2, 1}, step[4] = {1, 2, 2, 3};
19    set<string> s;
20    for (int state = 0; state < 16; state++) {
21        int d = __builtin_popcount(state);
22        if (d > c || (d + c) & 1) continue;
23        vector<bool> lamp(1 + n);
24        for (int r = 0; r < 4; r++)
25            if ((1 << r) & state)
26                for (int i = start[r]; i <= n; i += step[r])
27                    lamp[i] = !lamp[i];
28        bool success = true;
29        for (int i : on)
30            if (lamp[i]) success = false;
31        for (int i : off)
32            if (!lamp[i]) success = false;
33        if (!success) continue;
34        string str = "";
35        for (int i = 1; i <= n; i++) str += "10"[lamp[i]];
36        s.insert(str);
37    }
38    if (s.empty()) cout << "IMPOSSIBLE\n";
39    else for (string str : s) cout << str << "\n";
40    return 0;
41}

2.3 Longest Prefix

  • 題意:給定一個字串集合,大小 n ≤ 200,元素長度小魚 L ≤ 10,以及一個字串 S。問能用集合中元素組成的最長 S 前綴有多長?
  • 解法:DP,O(nL|S|)。
 1/*
 2ID: whitech1
 3TASK: prefix
 4LANG: C++14               
 5*/
 6#include <bits/stdc++.h>
 7using namespace std;
 8int main() {
 9    freopen("prefix.in", "r", stdin);
10    freopen("prefix.out", "w", stdout);
11    string tmp;
12    vector<string> pr;
13    while (cin >> tmp && tmp != ".") pr.push_back(tmp);
14    string s = " ";
15    while (cin >> tmp) s += tmp;
16    vector<bool> dp(s.length() + 1);
17    int ans = 0;
18    dp[0] = true;
19    for (int i = 1; s[i]; i++) {
20        for (string p : pr) {
21            int l = p.length();
22            if (i >= l && p == s.substr(i - l + 1, l))
23                dp[i] = dp[i] || dp[i - l];
24            if (dp[i]) {ans = i; break;}
25        }
26    }
27    cout << ans << "\n";
28    return 0;
29}

2.3 Cow Pedigrees

  • 題意:給定 N < 200 和 K < 100,每個點只能有 0 / 2 個子節點,求大小為 N 且深度為 K 的樹有多少種,答案 mod 9901。
  • 解法:DP。
    • 狀態:dp[n][k] 代表大小為 n 且深度不超過 k 的樹有幾種。
    • 轉移:O(N) 枚舉左右子樹的大小。
 1/*
 2ID: whitech1
 3TASK: nocows
 4LANG: C++14               
 5*/
 6#include <bits/stdc++.h>
 7using namespace std;
 8#define mod 9901
 9inline void add(int &a, int b) { a = (a + b) % mod; }
10int main() {
11    freopen("nocows.in", "r", stdin);
12    freopen("nocows.out", "w", stdout);
13    int n, k; scanf("%d%d", &n, &k);
14    int dp[200][100] = {};
15    for (int i = 1; i <= k; i++) dp[1][i] = 1;
16    for (int i = 1; i <= n; ++i)
17        for (int j = 1; j <= k; ++j)
18            for (int l = 1, r = i - 2; r >= 1; ++l, --r)
19                add(dp[i][j], dp[l][j - 1] * dp[r][j - 1]);
20    printf("%d\n", (dp[n][k] - dp[n][k - 1] + mod) % mod);
21    return 0;
22}

2.3 Zero Sum

  • 題意:給定 N ≤ 9,在 1 ~ N 之間插入 + - ,問有哪些算式能得到 0?
    • Ex. 1-2 3-4 5+6 7 = 1 - 23 - 45 + 67 = 0
  • 解法:枚舉所有可能,用 python 的 eval() 計算。
  • 複雜度:$$O(N3^N)$$
 1"""
 2ID: whitech1
 3LANG: PYTHON3
 4TASK: zerosum
 5"""
 6ans = []
 7def enum(i, s, n):
 8    s += str(i)
 9    if i == n:
10        if eval(s.replace(' ', '')) == 0:
11            ans.append(s + '\n')
12    else:
13        for op in " +-":
14            enum(i + 1, s + op, n)
15fin = open ('zerosum.in', 'r')
16fout = open ('zerosum.out', 'w')
17n = int(fin.readline())
18enum(1, '', n)
19fout.writelines(ans)
20fin.close()
21fout.close()

2.3 Money Systems

  • 題意:給定 V ≤ 25 種貨幣,問有幾種方法能組出 N ≤ 10000?
  • 解法:無限 DP。
 1/*
 2ID: whitech1
 3TASK: money
 4LANG: C++14               
 5*/
 6#include <bits/stdc++.h>
 7using namespace std;
 8int main() {
 9    freopen("money.in", "r", stdin);
10    freopen("money.out", "w", stdout);
11    int v, n; cin >> v >> n;
12    vector<long long> dp(n + 1);
13    dp[0] = 1;
14    while (v--) {
15        int x; cin >> x;
16        for (int i = x; i <= n; i++)
17            dp[i] += dp[i - x];
18    }
19    cout << dp[n] << '\n';
20    return 0;
21}

2.3 Controlling Companies

  • 題意:給定至多 m = 100 家公司,及他們的持股資訊。A 控制 B,當達到三種條件的其中一種,問共有幾種控制的 pair。
    1. A = B
    2. A 持有 B 的股票超過 50%
    3. A 控制的公司們持有 B 的股票和超過 50%
  • 解法:當 x 控制 y 時,則將 y 持有的股票加到 x,當超過 50,則繼續 DFS。因為最多 O(m^2) 次,每次 O(m),總時間複雜度 O(m^3)。
 1#include <bits/stdc++.h>
 2using namespace std;
 3typedef pair<int, int> pii;
 4bool cont[101][101];
 5int block[101][101];
 6void dfs(int i, int j) {
 7    if (cont[i][j]) return;
 8    cont[i][j] = true;
 9    for (int k = 1; k <= 100; k++) {
10        block[i][k] += block[j][k];
11        if (block[i][k] > 50) dfs(i, k);
12    }
13}
14int main() {
15    freopen("concom.in", "r", stdin);
16    freopen("concom.out", "w", stdout);
17    int n; cin >> n;
18    queue<int> q;
19    while (n--) {
20        int i, j, p; cin >> i >> j >> p;
21        block[i][j] = p;
22    }
23    for (int i = 1; i <= 100; i++)
24        for (int j = 1; j <= 100; j++)
25            if (block[i][j] > 50) dfs(i, j);
26    for (int i = 1; i <= 100; i++)
27        for (int j = 1; j <= 100; j++)
28            if (i != j && cont[i][j])
29                cout << i << " " << j << "\n";
30    return 0;
31}

2.4 The Tamworth Two

  • 題意:給定一個 10 x 10 的地圖,Farmer 和 Cow 以同樣的方式行走,他們一開始皆面向北,每分鐘可行走一步,當遇到障礙物時,則花一分鐘順時鐘轉 90 度,請問要多久兩者才會相遇在同一格?若不相遇則輸出 0。
  • 解法:模擬。最多有 10 x 10 x 4 = 400 種狀態,兩者則有 400 x 400 = 160000 種狀態,若他們並未相遇,則不可能會相遇。
 1#include <bits/stdc++.h>
 2using namespace std;
 3char board[12][12];
 4const int dx[4] = {-1, 0, 1, 0}, dy[4] = {0, 1, 0, -1};
 5struct state {
 6    int x, y, d;
 7    state() {d = 0;}
 8    state(int _x, int _y, int _d) {
 9        x = _x, y = _y, d = _d;
10    }
11};
12inline bool same(state A, state B) {
13    return A.x == B.x && A.y == B.y;
14}
15inline bool ob(int i, int j) {
16    return !board[i][j] || board[i][j] == '*';
17}
18state next_state(state S) {
19    int xx = S.x + dx[S.d], yy = S.y + dy[S.d];
20    if (ob(xx, yy)) return state(S.x, S.y, (S.d + 1) % 4);
21    return state(xx, yy, S.d);
22}
23int main() {
24    freopen("ttwo.in", "r", stdin);
25    freopen("ttwo.out", "w", stdout);
26    state F, C;
27    for (int i = 1; i <= 10; i++) {
28        scanf(" %s", board[i] + 1);
29        for (int j = 1; j <= 10; j++)
30            if (board[i][j] == 'F') F.x = i, F.y = j;
31            else if (board[i][j] == 'C') C.x = i, C.y = j;
32    }
33    for (int i = 0; i <= 160000; i++) {
34        if (same(F, C)) {
35            cout << i << "\n";
36            return 0;
37        }
38        F = next_state(F), C = next_state(C);
39    }
40    cout << "0\n";
41    return 0;
42}

2.4 Overfencing

  • 題意:給定一個 W x H 的地圖,W ≤ 38,H ≤ 100,最外面有兩個柵欄是開的,每個格子都能走到外圍,問離外圍最遠的格子有多遠?
  • 解法:從外圍的點開始 BFS,複雜度 O(WH)。小技巧是在最外圍加上一圈障礙物,就不會 BFS 超出陣列範圍。
 1#include <bits/stdc++.h>
 2using namespace std;
 3const int dx[4] = {0, 0, 1, -1}, dy[4] = {1, -1, 0, 0};
 4int main() {
 5    freopen("maze1.in", "r", stdin);
 6    freopen("maze1.out", "w", stdout);
 7    int w, h; cin >> w >> h; cin.ignore();
 8    char c[205][80];
 9    memset(c, '.', sizeof(c)); // 加上障礙物
10    for (int i = 2; i <= 2 * h + 2; i++)
11        cin.getline(c[i] + 2, 2 * w + 2);
12    int vst[205][80];
13    memset(vst, -1, sizeof(vst)); // 設為沒走過
14    queue<int> qx, qy;
15    // 放入外圈的點
16    for (int i = 1; i <= 2 * w + 3; i++) {
17        c[1][i] = c[2 * h + 3][i] = ' ';
18        vst[1][i] = vst[2 * h + 3][i] = 0;
19        qx.push(1), qy.push(i);
20        qx.push(2 * h + 3), qy.push(i);
21    }
22    for (int i = 1; i <= 2 * h + 3; i++) {
23        c[i][1] = c[i][2 * w + 3] = ' ';
24        vst[i][1] = vst[i][2 * w + 3] = 0;
25        qx.push(i), qy.push(1);
26        qx.push(i), qy.push(2 * w + 3);
27    }
28    int mx = 0;
29    // BFS
30    while (!qx.empty()) {
31        int x = qx.front(); qx.pop();
32        int y = qy.front(); qy.pop();
33        mx = vst[x][y]; // 最後走到的點就是答案
34        for (int d = 0; d < 4; d++) {
35            int xx = dx[d] + x, yy = dy[d] + y;
36            // 遇到障礙物或走過的點就跳過
37            if (c[xx][yy] != ' ' || vst[xx][yy] != -1) continue;
38            vst[xx][yy] = vst[x][y] + 1;
39            qx.push(xx), qy.push(yy);
40        }
41    }
42    cout << mx / 2 << '\n';
43    return 0;
44}

2.4 Cow Tours

  • 題意:給定一個大小為 N ≤ 150 的圖,這個圖為加上一條邊可聯通的非聯通圖。問加上一條邊後的最小直徑(diameter)為多少。
  • 解法:先做一次 Floyd-Warshall 算出枚舉加入 (i, j),i、j 兩聯通塊和 i、j 最遠的點距和 i、j 的距離相加,再和未聯通前的最大直徑比較。
 1#include <bits/stdc++.h>
 2using namespace std;
 3const double INF = 40000000;
 4inline double dst(int x, int y, int xx, int yy) {
 5    double dx = x - xx, dy = y - yy;
 6    return sqrt(dx * dx + dy * dy);
 7}
 8int main() {
 9    freopen("cowtour.in", "r", stdin);
10    freopen("cowtour.out", "w", stdout);
11    ios::sync_with_stdio(0), cin.tie(0);
12    int n; cin >> n;
13    vector<int> x(n), y(n);
14    for (int i = 0; i < n; i++) cin >> x[i] >> y[i];
15    vector<string> m(n);
16    for (int i = 0; i < n; i++) cin >> m[i];
17    vector<vector<double> > dis(n, vector<double>(n, 0));
18    for (int i = 0; i < n; i++)
19        for (int j = i + 1; j < n; j++)
20            dis[i][j] = dis[j][i] = dst(x[i], y[i], x[j], y[j]);
21    vector<vector<double> > dp(n, vector<double>(n, INF));
22    for (int i = 0; i < n; i++) {
23        dp[i][i] = 0;
24        for (int j = i + 1; j < n; j++)
25            if (m[i][j] == '1') dp[i][j] = dp[j][i] = dis[i][j];
26    }
27    for (int i = 0; i < n; i++)
28        for (int j = 0; j < n; j++)
29            for (int k = 0; k < n; k++)
30                dp[j][k] = min(dp[j][k], dp[j][i] + dp[i][k]);
31    vector<double> far(n, 0);
32    for (int i = 0; i < n; i++)
33        for (int j = 0; j < n; j++)
34            if (dp[i][j] + 1 < INF)
35                far[i] = max(far[i], dp[i][j]);
36    double ans = INF;
37    for (int i = 0; i < n; i++)
38        for (int j = i + 1; j < n; j++)
39            if (dp[i][j] + 1 > INF)
40                ans = min(ans, far[i] + far[j] + dis[i][j]);
41    for (int i = 0; i < n; i++) ans = max(ans, far[i]);
42    cout << fixed << setprecision(6) << ans << "\n";
43    return 0;
44}

2.4 Bessie Come Home

  • 題意:共有 A ~ Y 和 a ~ z 這些牧場,每個大寫字母的牧場都有一隻牛,給定 P ≤ 10000 條道路,問距離 Z 最近的牧場為何?
  • 解法:Dijkstra 找最短距離,O(P log P)。
 1#include <bits/stdc++.h>
 2using namespace std;
 3typedef pair<int, int> pii;
 4const int INF = 100000000;
 5inline int id(char c) {
 6    if (isupper(c)) return c - 'A';
 7    return c - 'a' + 26;
 8}
 9int main() {
10    freopen("comehome.in", "r", stdin);
11    freopen("comehome.out", "w", stdout);
12    int m; cin >> m;
13    vector<vector<pii> > G(52);
14    while (m--) {
15        char a, b; int w; cin >> a >> b >> w;
16        G[id(a)].push_back(pii(w, id(b)));
17        G[id(b)].push_back(pii(w, id(a)));
18    }
19    vector<int> dis(52, INF);
20    dis[id('Z')] = 0;
21    priority_queue<pii, vector<pii>, greater<pii> > pq;
22    pq.push(pii(0, id('Z')));
23    while (!pq.empty()) {
24        int u = pq.top().second; pq.pop();
25        if (u < 25) {
26            cout << char(u + 'A') << ' ' << dis[u] << '\n';
27            return 0;
28        }
29        for (pii e : G[u]) {
30            int uu = e.second, w = e.first;
31            if (dis[uu] > dis[u] + w) {
32                dis[uu] = dis[u] + w;
33                pq.push(pii(dis[uu], uu));
34            }
35        }
36    }
37    return 0;
38}

2.4 Fractions to Decimals

  • 題意:給定 N 和 D ≤ 100000,求 N / Q 的循環小數表示。
  • 解法:當餘數重複出現,則表示出現循環,餘數至多 D 種,所以複雜度 O(D)。
 1#include <bits/stdc++.h>
 2using namespace std;
 3inline char dec(int x) {return x + '0';}
 4string str(int x) {
 5    string s = "";
 6    while (x) {
 7        s += char(x % 10 + '0');
 8        x /= 10;
 9    }
10    reverse(s.begin(), s.end());
11    return s;
12}
13void print(string s) {
14    for (int i = 0; s[i]; i++) {
15        cout << s[i];
16        if (i % 76 == 75 || i == s.length() - 1)
17            cout << "\n";
18    }
19}
20int main() {
21    freopen("fracdec.in", "r", stdin);
22    freopen("fracdec.out", "w", stdout);
23    ios::sync_with_stdio(0), cin.tie(0);
24    int N, D; cin >> N >> D;
25    int Q = N / D;
26    if (!N) {
27        cout << Q << ".0\n";
28        return 0;
29    }
30    N %= D;
31    vector<int> q, r;
32    vector<int> vst(D + 1, -1);
33    r.push_back(N), vst[N] = 0;
34    while (N) {
35        q.push_back(N * 10 / D);
36        r.push_back(N * 10 % D);
37        N = r.back();
38        if (vst[N] != -1) break;
39        vst[N] = q.size();
40    }
41    string ans = Q ? str(Q) : "0";
42    ans += ".";
43    int rep = vst[r.back()];
44    for (int i = 0; i < rep; i++) ans += dec(q[i]);
45    if (rep != r.size() - 1) {  
46        ans += "(";
47        for (int i = rep; i < q.size(); i++)
48            ans += dec(q[i]);
49        ans += ")";
50    }
51    print(ans);
52    return 0;
53}

Chapter 3

3.1 Agri-Net

  • 題意:給 N ≤ 100 的 Adjacency Matrix,求 MST。
  • 解法:做 Prim,複雜度 O(N^2)。
 1#include <bits/stdc++.h>
 2using namespace std;
 3#define MAXN 105
 4#define INF 100000000
 5int main() {
 6    freopen("agrinet.in", "r", stdin);
 7    freopen("agrinet.out", "w", stdout);
 8    int n; cin >> n;
 9    int G[MAXN][MAXN];
10    for (int i = 0; i < n; i++)
11        for (int j = 0; j < n; j++)
12            cin >> G[i][j];
13    int dis[MAXN] = {};
14    for (int i = 1; i < n; i++) dis[i] = INF;
15    bool visit[MAXN] = {};
16    int ans = 0;
17    for (int i = 0; i < n; i++) {
18        int u = 0, mx = INF;
19        for (int j = 1; j < n; j++)
20            if (!visit[j] && dis[j] < mx)
21                u = j, mx = dis[j];
22        visit[u] = true;
23        ans += dis[u];
24        for (int i = 0; i < 100; i++)
25            dis[i] = min(dis[i], G[u][i]);
26    }
27    cout << ans << "\n";
28    return 0;
29}

3.1 Score Inflation

  • 題意:裸的無限背包。
  • 解法:DP,O(MN)。
 1#include <bits/stdc++.h>
 2using namespace std;
 3#define MAXM 10005
 4int main() {
 5    freopen("inflate.in", "r", stdin);
 6    freopen("inflate.out", "w", stdout);
 7    int M, N; cin >> M >> N;
 8    int dp[MAXM] = {};
 9    while (N--) {
10        int w, t; cin >> w >> t;
11        for (int i = t; i <= M; i++)
12            dp[i] = max(dp[i], dp[i - t] + w);
13    }
14    cout << dp[M] << "\n";
15    return 0;
16}

3.1 Humble Numbers

  • 題意:給定一個大小為 K ≤ 100 的質數集合,問第 N ≤ 100000 個質因數只有集合裡元素的數為何(不含 1)?
  • 解法:如果用 priority_queue 會 MLE,用 set 會 TLE。用了 DP,不斷計算每個因數目前被用到最大的數為何,複雜度 O(NK)。
 1#include <bits/stdc++.h>
 2using namespace std;
 3int main() {
 4    freopen("humble.in", "r", stdin);
 5    freopen("humble.out", "w", stdout);
 6    int K, N; cin >> K >> N;
 7    vector<int> v(K);
 8    for (int i = 0; i < K; i++) cin >> v[i];
 9    vector<int> dp(N + 1);
10    dp[0] = 1;
11    vector<int> p(K);
12    for (int i = 1; i <= N; i++) {
13        dp[i] = dp[p[0]] * v[0];
14        for (int j = 1; j < K; j++)
15            dp[i] = min(dp[i], dp[p[j]] * v[j]);
16        for (int j = 0; j < K; j++)
17            if (dp[i] == dp[p[j]] * v[j]) p[j]++;
18    }
19    cout << dp[N] << "\n";
20    return 0;
21}

3.1 Contact

  • 題意:給定 1 ≤ a ≤ b ≤ 12 和 1 ≤ N ≤ 50,以及一個長度最長 200000 的 0/1 字串 S,問長度介於 [a, b] 的 0/1 字串和 S 有多少 match 次數,列出前 N 大的頻率的所有字串。
  • 解法:將所有長度為 [a, b] 的子字串前綴加上 1 後 hash 丟進 map 計算次數,O(b|S|)。
 1#include <bits/stdc++.h>
 2using namespace std;
 3typedef pair<int, string> pis;
 4typedef pair<int, pis> piis;
 5int a, b;
 6string p;
 7vector<piis> ans;
 8int mp[(1 << 13)];
 9void substring(string s) {
10    for (int r = a; r <= b; r++) {
11        int mask = 1 << r, now = 0;
12        for (int i = 0; s[i]; i++)  {
13            now = ((now << 1) | (s[i] - '0')) & (mask - 1);
14            if (i >= r - 1) mp[now | mask]++;
15        }
16    }
17}
18void solve(int len) {
19    int pat = 0;
20    for (int i = 0; p[i]; i++) pat = (pat << 1) | (p[i] - '0');
21    pat |= (1 << len);
22    if (a <= len && mp[pat])
23        ans.push_back(piis(-mp[pat], pis(len, p)));
24    if (len >= b) return;
25    p += '0'; solve(len + 1); p.pop_back();
26    p += '1'; solve(len + 1); p.pop_back();
27}
28int main() {
29    freopen("contact.in", "r", stdin);
30    freopen("contact.out", "w", stdout);
31    ios::sync_with_stdio(0), cin.tie(0);
32    int n; cin >> a >> b >> n;
33    string s, ss;
34    while (cin >> ss) s += ss;
35    substring(s);
36    solve(0);
37    sort(ans.begin(), ans.end());
38    int sz = ans.size();
39    for (int i = 0, j = 0; i < n && j < sz; i++) {
40        int f = ans[j].first;
41        cout << -f << "\n";
42        vector<string> tmp;
43        for (; ans[j].first == f && j < sz; j++)
44            tmp.push_back(ans[j].second.second);
45        for (int k = 0; k < tmp.size(); k++)
46            cout << tmp[k] << " \n"[k % 6 == 5 || k == tmp.size() - 1];
47    }
48    return 0;
49}

3.1 Stamps

  • 題意:給定 N ≤ 50 種價值的郵票,最大價值 M ≤ 10000,問最多只用 K ≤ 200 種郵票,能湊出的數為 1 ~ 多少?
  • 解法:DP。狀態 DP[i] 設為湊出 i 需要的最少數量,O(N) 轉移,複雜度為 O(MNK)。
 1#include <bits/stdc++.h>
 2using namespace std;
 3int main() {
 4    freopen("stamps.in", "r", stdin);
 5    freopen("stamps.out", "w", stdout);
 6    int k, n; cin >> k >> n;
 7    vector<int> v(n);
 8    int mx = 0;
 9    for (int i = 0; i < n; i++) {
10        cin >> v[i];
11        mx = max(mx, v[i]);
12    }
13    mx *= k;
14    vector<int> dp(mx + 2, k + 1);
15    dp[0] = 0;
16    for (int i = 0; i <= mx; i++)
17        for (int j = 0; j < n; j++)
18            if (i >= v[j])
19                dp[i] = min(dp[i], dp[i - v[j]] + 1);
20    int ans = 0;
21    for (; dp[ans] <= k; ans++);
22    cout << ans - 1 << "\n";
23    return 0;
24}

3.2 Factorials

  • 題意:給定 N ≤ 4220,問 N! 最右邊的非零數字是多少? Ex. 7! = 5040 是 4。
  • 解法:只有 2 和 5 會產生 0 結尾,把他們提出來另外處理,其他就不斷相乘模 10,最後將多出的 2 或 5 乘上去模 10,複雜度 O(N log N)。
 1#include <bits/stdc++.h>
 2using namespace std;
 3int main() {
 4    freopen("fact4.in", "r", stdin);
 5    freopen("fact4.out", "w", stdout);
 6    int n; cin >> n;
 7    int ans = 1, two = 0, five = 0;
 8    for (int i = 1; i <= n; i++) {
 9        int x = i;
10        while (x % 2 == 0) x /= 2, two++;
11        while (x % 5 == 0) x /= 5, five++;
12        ans = ans * x % 10;
13    }
14    for (int i = two; i < five; i++) ans = ans * 5 % 10;
15    for (int i = five; i < two; i++) ans = ans * 2 % 10;
16    cout << ans << "\n";
17    return 0;
18}

3.2 Stringsobits

  • 題意:給定 N ≤ 31,集合 S 包含所有 1-bit ≤ L 個的 N-bit 數,問集合 S 中的第 I 個數?
  • 解法:遞迴檢查前綴為 s 的數在集合中有幾個,這部分可用排列組合計算,這樣就可以決定每一位要放 0 或 1。C(a, b) 的部分是用不斷消去分子和分母的最大公因數。複雜度為 O(N^4)。要小心 I 最大為 2^31,要用 unsigned int。官方解答用 DP 算出 C(a, b),複雜度可降到 O(N^2)。
 1#include <bits/stdc++.h>
 2using namespace std;
 3typedef unsigned int ui;
 4string s;
 5int C(int a, int b) {
 6    if (a < b) return 0;
 7    vector<int> up, down;
 8    for (int i = a; i > b; i--) up.push_back(i);
 9    for (int i = a - b; i > 1; i--) down.push_back(i);
10    for (int i = 0; i < down.size(); i++) {
11        for (int j = 0; j < up.size(); j++) {
12            int g = __gcd(down[i], up[j]);
13            down[i] /= g, up[j] /= g;
14        }
15    }
16    int ret = 1;
17    for (int x : up) ret *= x;
18    return ret;
19}
20void solve(int len, int cnt, ui id) {
21    if (!len) return;
22    ui f = 0;
23    for (int i = cnt; i >= 0; i--) f += C(len - 1, i);
24    if (f < id) {
25        s += '1';
26        solve(len - 1, cnt - 1, id - f);
27    } else {
28        s += '0';
29        solve(len - 1, cnt, id);
30    }
31}
32int main() {
33    freopen("kimbits.in", "r", stdin);
34    freopen("kimbits.out", "w", stdout);
35    int N, L; ui I; cin >> N >> L >> I;
36    solve(N, L, I);
37    cout << s << "\n";
38    return 0;
39}

3.2 Spinning Wheels

  • 讀題讀了很久,最後直接 Google 中文翻譯。
 1#include <bits/stdc++.h>
 2using namespace std;
 3typedef pair<int, int> pii;
 4int main() {
 5    freopen("spin.in", "r", stdin);
 6    freopen("spin.out", "w", stdout);
 7    int speed[5];
 8    vector<pii> w[5];
 9    for (int i = 0; i < 5; i++) {
10        int x; cin >> speed[i] >> x;
11        w[i].resize(x);
12        for (int j = 0; j < x; j++) {
13            cin >> w[i][j].first >> w[i][j].second;
14            w[i][j].second += w[i][j].first;
15        }
16    }
17    int ans = -1;
18    for (int t = 0; t < 360 && ans == -1; t++) {
19        int check[360] = {};
20        for (int i = 0; i < 5; i++)
21            for (pii p : w[i])
22                for (int j = p.first; j <= p.second; j++)
23                    check[(j + t * speed[i]) % 360]++;
24        for (int i = 0; i < 360; i++)
25            if (check[i] == 5) {ans = t; break;}
26    }
27    if (ans == -1) cout << "none\n";
28    else cout << ans << "\n";
29    return 0;
30}

3.2 Feed Ratios

  • 暴力枚舉題。
 1#include <bits/stdc++.h>
 2using namespace std;
 3int main() {
 4    freopen("ratios.in", "r", stdin);
 5    freopen("ratios.out", "w", stdout);
 6    int mi = 1000;
 7    int a[4][3];
 8    for (int i = 0; i < 4; i++)
 9        for (int j = 0; j < 3; j++)
10            cin >> a[i][j];
11    int c[4], ans[4];
12    for (c[1] = 0; c[1] < 100; ++c[1]) {
13        for (c[2] = 0; c[2] < 100; ++c[2]) {
14            for (c[3] = 0; c[3] < 100; ++c[3]) {
15                int cnt[3] = {}, t[3];
16                bool f = true;
17                int mx = 0;
18                for (int i = 0; i < 3; i++) {
19                    for (int j = 1; j <= 3; j++) cnt[i] += c[j] * a[j][i];
20                    if (!a[0][i] && !cnt[i]) t[i] = 0;
21                    else if ((!a[0][i] && cnt[i]) || cnt[i] % a[0][i]) f = false;
22                    else if (a[0][i]) t[i] = cnt[i] / a[0][i];
23                    else t[i] = 0;
24                    mx = max(mx, t[i]);
25                }
26                for (int i = 0; i < 3; i++)
27                    if (mx * a[0][i] != cnt[i])
28                        f = false;
29                if (f && (c[0] + c[1] + c[2]))
30                    if (mi > c[0] + c[1] + c[2])
31                        mi = c[0] + c[1] + c[2],
32                        ans[0] = mx, ans[1] = c[1],
33                        ans[2] = c[2], ans[3] = c[3];
34                        
35            }
36        }
37    }
38    if (mi < 1000) {
39        for (int i = 1; i < 4; i++) cout << ans[i] << " ";
40        cout << ans[0] << "\n";
41    }
42    else cout << "NONE\n";
43    return 0;
44}

3.2 Magic Squares

  • 題意:給定一個由 1 ~ 8 組成的 4 x 2 矩陣,及三種循環操作,求產生目標矩陣的最小操作數方法為何?
  • 解法:BFS,因為狀態最多 8! 種,將狀態用 8 位整數儲存到 map 裡。
 1#include <bits/stdc++.h>
 2using namespace std;
 3int c[3][8] = {{4, 5, 6, 7, 0, 1, 2, 3},
 4               {3, 0, 1, 2, 7, 4, 5, 6},
 5               {0, 5, 1, 3, 4, 6, 2, 7}};
 6char ch[4] = "ABC";
 7int change(int x, int r) {
 8    int a[8] = {};
 9    for (int i = 0; i < 8; i++) {
10        a[7 - i] = x % 10;
11        x /= 10;
12    }
13    int ret = 0;
14    for (int i = 0; i < 8; i++) ret = ret * 10 + a[c[r][i]];
15    return ret;
16}
17int main() {
18    freopen("msquare.in", "r", stdin);
19    freopen("msquare.out", "w", stdout);
20    int goal = 0;
21    int a[8];
22    for (int i = 0; i < 8; i++) cin >> a[i];
23    for (int i = 0; i < 4; i++) goal = goal * 10 + a[i];
24    for (int i = 7; i >= 4; i--) goal = goal * 10 + a[i];
25    map<int, string> m;
26    queue<int> state;
27    state.push(12348765);
28    m[12348765] = "";
29    while (!state.empty()) {
30        int s = state.front(); state.pop();
31        if (s == goal) break;
32        for (int i = 0; i < 3; i++) {
33            int ss = change(s, i);
34            if (m.find(ss) != m.end()) continue;
35            m[ss] = m[s] + ch[i];
36            state.push(ss);
37        }
38    }
39    cout << m[goal].length() << "\n" << m[goal] << "\n";
40    return 0;
41}

3.2 Sweet Butter

  • 題意:給定 N ≤ 500 頭牛,共有 P ≤ 800 個農場和 C ≤ 1450 條路,問要將集合點設在哪,才能使牛隻們走的總距離最小?
  • 解法:以每個點當起點做一次最短路,這邊用 Dijkstra,總複雜度 O(PC log C)。
 1#include <bits/stdc++.h>
 2using namespace std;
 3typedef pair<int, int> pii;
 4const int INF = 2000000000;
 5int main() {
 6    freopen("butter.in", "r", stdin);
 7    freopen("butter.out", "w", stdout);
 8    int n, p, c; cin >> n >> p >> c;
 9    vector<int> v(n);
10    vector<vector<pii> > G(p + 1);
11    for (int i = 0; i < n; i++) cin >> v[i];
12    while (c--) {
13        int a, b, w; cin >> a >> b >> w;
14        G[a].push_back(pii(w, b));
15        G[b].push_back(pii(w, a));
16    }
17    int ans = INF;
18    for (int i = 1; i <= p; i++) {
19        vector<int> dis(p + 1, INF);
20        priority_queue<pii, vector<pii>, greater<pii> > pq;
21        dis[i] = 0, pq.push(pii(0, i));
22        while (!pq.empty()) {
23            int u = pq.top().second; pq.pop();
24            for (pii e : G[u]) {
25                int uu = e.second, w = e.first;
26                if (dis[uu] > dis[u] + w) {
27                    dis[uu] = dis[u] + w;
28                    pq.push(pii(dis[uu], uu));
29                }
30            }
31        }
32        int cnt = 0;
33        for (int x : v) cnt += dis[x];
34        ans = min(ans, cnt);
35    }
36    cout << ans << "\n";
37    return 0;
38}

3.3 Riding the Fences

  • 題意:給定一個 500 個點以內無向的聯通圖,求找出 Euler Path/Circuit。
  • 解法:如果有奇點就從它開始 DFS,否則從編號最小的點開始。
 1#include <bits/stdc++.h>
 2using namespace std;
 3#define MAXN 505
 4int G[MAXN][MAXN], deg[MAXN];
 5vector<int> ans;
 6void solve(int i) {
 7    for (int j = 0; j < MAXN && deg[i]; j++)
 8        while (G[i][j]) {
 9            deg[j]--, deg[i]--, G[i][j]--, G[j][i]--;
10            solve(j);
11        }
12    ans.push_back(i);
13}
14int main() {
15    freopen("fence.in", "r", stdin);
16    freopen("fence.out", "w", stdout);
17    int F; cin >> F;
18    for (int i = 0; i < F; i++) {
19        int a, b; cin >> a >> b;
20        deg[a]++, deg[b]++, G[a][b]++, G[b][a]++;
21    }
22    for (int i = 0; i < MAXN; i++)
23        if (deg[i] & 1) { solve(i); break;}
24    for (int i = 0; i < MAXN; i++)
25        if (deg[i]) { solve(i); break;}
26    for (int i = ans.size() - 1; i >= 0; i--)
27        cout << ans[i] << "\n";
28    return 0;
29}

3.3 Shopping Offers

  • 題意:給定 b ≤ 5 種東西及各自的單價和需要的數量 ≤ 5,以及 s ≤ 99 種組合,每種組合包含數種物品、數量及組合價,問能買到剛好所需物品的最低價格為何?
  • 解法:DP,i 為把 b 種物品的量 hash 起來,DP[i] 表示其最低所需價格。
  • 寫得好醜 QAQ
 1#include <bits/stdc++.h>
 2using namespace std;
 3typedef pair<int, int> pii;
 4int num(int id, int state) {
 5    for (int i = 0; i < id; i++) state /= 10;
 6    return state %= 10;
 7}
 8int main() {
 9    freopen("shopping.in", "r", stdin);
10    freopen("shopping.out", "w", stdout);
11    ios::sync_with_stdio(0), cin.tie(0);
12    int s; cin >> s;
13    vector<vector<pii> > v(s);
14    vector<int> p(s);
15    for (int i = 0; i < s; i++) {
16        int n; cin >> n;
17        v[i].resize(n);
18        for (int j = 0; j < n; j++)
19            cin >> v[i][j].first >> v[i][j].second;
20        cin >> p[i];
21    }
22    int b; cin >> b;
23    vector<pii> r(b);
24    vector<int> dp(55556);
25    for (int i = 0; i < b; i++) {
26        int pp; cin >> r[i].first >> r[i].second >> pp;
27        for (int j = dp.size() - 1; j; j--) {
28            int cnt = num(i, j);
29            if (cnt <= r[i].second) dp[j] += cnt * pp;
30        }
31    }
32    int goal = 0;
33    for (int i = b - 1; i >= 0; i--)
34        goal = goal * 10 + r[i].second;
35    for (int i = 0; i < goal; i++) {
36        vector<int> state(b);
37        for (int j = 0; j < b; j++) state[j] = num(j, i);
38        for (int vi = 0; vi < s; vi++) {
39            bool success = true;
40            vector<int> new_state(state.begin(), state.end());
41            for (pii p : v[vi]) {
42                int id = -1;
43                for (int ii = 0; ii < b; ii++)
44                    if (p.first == r[ii].first) id = ii;
45                if (id == -1) success = false;
46                new_state[id] += p.second;
47            }
48            for (int i = 0; i < b; i++)
49                if (new_state[i] > r[i].second) success = false;
50            if (!success) continue;
51            int st = 0;
52            for (int j = b - 1; j >= 0; j--)
53                st = st * 10 + new_state[j];
54            dp[st] = min(dp[st], dp[i] + p[vi]);
55        }
56    }
57    cout << dp[goal] << "\n";
58    return 0;
59}

3.3 Camelot

  • 寫的有點醜醜的,但其實不難寫,想的難度比較高。因為想不太到夠快的方法,所以就 Google 了一下。
  • 題意:給定一個 R x C ≤ 30 x 26 的棋盤,棋盤上有一個 King 和多個 Knights,棋子可停留在同一格,當 King 和 Knights 在一起時,King 可帶著一個 Knight 照著 Knight 的走法走,問使得所有棋子都在同一格的最少步數?
  • 解法:如果沒有 King 的話,這題就是對每個點做一次 BFS,最小距離和就是答案。而多了 King 的做法,就是將每個棋做一次 SPFA,紀錄到每一格以遇到 King 或未遇到的最小步數。
  • 複雜度:假設棋子數量為 K。$$O(KRC)$$
 1#include <bits/stdc++.h>
 2using namespace std;
 3typedef pair<int, int> pii;
 4#define x first
 5#define y second
 6#define INF 100000000
 7inline int step(pii k, pii p) {
 8    return max(abs(k.x - p.x), abs(k.y - p.y));
 9}
10inline bool range(pii p, int R, int C) {
11    return 0 <= p.x && 0 <= p.y && p.x < R && p.y < C;
12}
13int dis[1000][30][35][2];
14int main() {
15    freopen("camelot.in", "r", stdin);
16    freopen("camelot.out", "w", stdout);
17    int R, C;
18    cin >> R >> C;
19    int tmp_int;
20    char tmp_ch;
21    cin >> tmp_ch >> tmp_int;
22    pii king = {tmp_int - 1, tmp_ch - 'A'};
23    vector<pii> knight;
24    while (cin >> tmp_ch >> tmp_int)
25        knight.push_back({tmp_int - 1, tmp_ch - 'A'});
26    if (!knight.size()) {
27        cout << "0\n";
28        return 0;
29    }
30    for (int i = 0; i < knight.size(); i++)
31        for (int j = 0; j < R; j++)
32            for (int k = 0; k < C; k++)
33                dis[i][j][k][0] = dis[i][j][k][1] = INF;
34    const int dx[8] = {2, 2, 1, 1, -1, -1, -2, -2};
35    const int dy[8] = {1, -1, 2, -2, 2, -2, 1, -1};
36    for (int i = 0; i < knight.size(); i++) {
37        dis[i][knight[i].x][knight[i].y][0] = 0;
38        dis[i][knight[i].x][knight[i].y][1] = step(king, knight[i]);
39        queue<pii> q;
40        q.push(knight[i]);
41        while (!q.empty()) {
42            int xx = q.front().x, yy = q.front().y;
43            q.pop();
44            for (int di = 0; di < 8; di++) {
45                int a = xx + dx[di], b = yy + dy[di];
46                if (!range({a, b}, R, C)) continue;
47                bool inque = false;
48                if (dis[i][a][b][0] > dis[i][xx][yy][0] + 1) {
49                    dis[i][a][b][0] = dis[i][xx][yy][0] + 1;
50                    inque = true;
51                }
52                int d = min(dis[i][xx][yy][0] + step(king, {a, b}),
53                            dis[i][xx][yy][1]) + 1;
54                if (dis[i][a][b][1] > d) {
55                    dis[i][a][b][1] = d;
56                    inque = true;
57                }
58                if (inque) q.push({a, b});
59            }
60        }
61    }
62    int ans = INF;
63    for (int i = 0; i < R; i++) {
64        for (int j = 0; j < C; j++) {
65            int mi = INF, sum = 0;
66            for (int k = 0; k < knight.size(); k++) {
67                sum += dis[k][i][j][0];
68                mi = min(mi, dis[k][i][j][1] - dis[k][i][j][0]);
69            }
70            ans = min(ans, sum + mi);
71        }
72    }
73    cout << ans << "\n";
74    return 0;
75}

3.4 American Heritage

  • 題意:給定一棵二元樹的中序和前序,求輸出後序。
  • 解法:用前序標出中間節點後遞迴。應該可以做到 O(n),但因為懶,所以用了 O(n^2)。
 1#include <bits/stdc++.h>
 2using namespace std;
 3string pre, in;
 4void solve(int il, int ir, int pl, int pr) {
 5    if (il > ir) return;
 6    if (il == ir) { cout << in[il]; return; }
 7    int i;
 8    for (i = il; in[i] != pre[pl]; i++) {}
 9    solve(il, i - 1, pl + 1, pl - il + i);
10    solve(i + 1, ir, pr - ir + i + 1, pr);
11    cout << pre[pl];
12}
13int main() {
14    freopen("heritage.in", "r", stdin);
15    freopen("heritage.out", "w", stdout);
16    cin >> in >> pre;
17    solve(0, in.length() - 1, 0, in.length() - 1);
18    cout << "\n";
19    return 0;
20}

3.4 Electric Fence

  • 題意:給定一個三角形,頂點為 (0, 0), (n, m), (p, 0),介於 [0, 32000]x[0, 32000] 之間,問有幾個座標點是在三角形裡?
  • 解法:對每個 y 都算一次邊上的座標,複雜度 O(m)。
  • 官方解法:Pick’s Theorem。
 1#include <bits/stdc++.h>
 2using namespace std;
 3int main() {
 4    freopen("fence9.in", "r", stdin);
 5    freopen("fence9.out", "w", stdout);
 6    int n, m, p;
 7    cin >> n >> m >> p;
 8    int ans = 0;
 9    for (int i = 1; i < m; i++)
10        ans += max(0, ((n + p) * i - 1) / m - n * i / m);
11    cout << ans << "\n";
12    return 0;
13}

3.4 Raucous Rockers

  • 題意:給定 n ≤ 20 首個歌的長度,共有 m ≤ 20 個唱片可以存放歌曲,每張唱片最多存 T ≤ 20 的長度,且存放時要按照輸入的順序,問最多能存幾首歌。
  • 解法:枚舉放了哪些歌,然後 Greedy,O(n 2^n)。或是用 DP 做,O(nmT)。
 1#include <bits/stdc++.h>
 2using namespace std;
 3int main() {
 4    freopen("rockers.in", "r", stdin);
 5    freopen("rockers.out", "w", stdout);
 6    int n, m, t;
 7    cin >> n >> t >> m;
 8    vector<int> v(n);
 9    for (int i = 0; i < n; i++) cin >> v[i];
10    int N = 1 << n;
11    int ans = 0;
12    for (int i = 1; i < N; i++) {
13        int c = __builtin_popcount(i);
14        if (c <= ans) continue;
15        int r = 0, cnt = 0;
16        for (int j = 0; j < n; j++) {
17            if ((1 << j) & i) {
18                if (v[j] > t) r = m;
19                if (cnt + v[j] > t)
20                    r++, cnt = v[j];
21                else cnt += v[j];
22            }
23        }
24        if (r < m) ans = max(c, ans);
25    }
26    cout << ans << "\n";
27    return 0;
28}

Chapter 4

4.1 Beef McNuggets

  • 有點吃數論的題目~
  • 題意:給定 N ≤ 10 個正整數 ≤ 256,求最大的正整數不能用這些數字組出來,若無限大則輸出 0。
  • 解法:若 gcd 不為 1 則輸出 0,否則答案會在 256 x 256 以內,DP 解即可。
    • 對於互質的正整數 a, b,最大不能得到的數為 ab - a - b。
 1#include <bits/stdc++.h>
 2using namespace std;
 3int main() {
 4    freopen("nuggets.in", "r", stdin);
 5    freopen("nuggets.out", "w", stdout);
 6    int n; cin >> n;
 7    vector<int> a(n);
 8    for (int i = 0; i < n; i++) cin >> a[i];
 9    int g = a[0];
10    for (int x : a) g = __gcd(x, g);
11    if (g != 1) {
12        cout << "0\n";
13        return 0;
14    }
15    const int M = 256 * 256;
16    bool check[M + 300] = {1};
17    int ans = 0;
18    for (int i = 0; i < M; i++)
19        if (check[i])
20            for (int x : a)
21                check[i + x] = true;
22        else ans = i;
23    cout << ans << "\n";
24    return 0;
25}
Licensed under CC BY-NC-ND
Built with Hugo
Theme Stack designed by Jimmy