|
| 1 | +--- |
| 2 | +title: 1208F - Bits And Pieces |
| 3 | +category: notes |
| 4 | +date: 2026-02-24 |
| 5 | +tags: [bitmasks, dp] |
| 6 | +--- |
| 7 | + |
| 8 | +### [1208F - Bits And Pieces](https://codeforces.com/problemset/problem/1208/F) |
| 9 | + |
| 10 | +Fix $a[i]$, then we can enumerate a prefix of off-bits in $a[i]$. The remaining task is to find the two right most positions that contain the prefix we enumerate. |
| 11 | + |
| 12 | +This can be solved by SOS DP. Let $f[s]$ and $g[s]$ be the first and second right most positions that contain $s$. |
| 13 | + |
| 14 | +```cpp |
| 15 | +#include <cmath> |
| 16 | +#include <vector> |
| 17 | +#include <iostream> |
| 18 | +#include <algorithm> |
| 19 | + |
| 20 | +void insert(int &first_max, int &second_max, int val) { |
| 21 | + if (val > first_max) { |
| 22 | + second_max = first_max; |
| 23 | + first_max = val; |
| 24 | + } else if (val < first_max && val > second_max) { |
| 25 | + second_max = val; |
| 26 | + } |
| 27 | +} |
| 28 | + |
| 29 | +int main() { |
| 30 | + std::cin.tie(nullptr)->sync_with_stdio(0); |
| 31 | + int n; |
| 32 | + std::cin >> n; |
| 33 | + // fix a[i] |
| 34 | + // enumerate prefix off-bits of a[i] |
| 35 | + // f[s] = rightmost a[i] s.t. s \in a[i] |
| 36 | + // g[s] = second rightmost |
| 37 | + std::vector<int> a(n); |
| 38 | + for (auto &x : a) std::cin >> x; |
| 39 | + int max_val = *std::max_element(a.begin(), a.end()); |
| 40 | + int max_bit = std::log2(max_val) + 1; |
| 41 | + |
| 42 | + std::vector<int> f(1 << max_bit, -1), g(1 << max_bit, -1); |
| 43 | + for (int i = n; i--;) { |
| 44 | + int x = a[i]; |
| 45 | + if (f[x] == -1) f[x] = i; |
| 46 | + else if (g[x] == -1) g[x] = i; |
| 47 | + } |
| 48 | + // g[t] < f[t] |
| 49 | + // f[t] <- f[s] |
| 50 | + for (int i = 0; i < max_bit; ++i) { |
| 51 | + for (int s = 0; s < (1 << max_bit); ++s) { |
| 52 | + if (s >> i & 1) { |
| 53 | + int t = s ^ (1 << i); |
| 54 | + insert(f[t], g[t], f[s]); |
| 55 | + insert(f[t], g[t], g[s]); |
| 56 | + } |
| 57 | + } |
| 58 | + } |
| 59 | + int ans = 0; |
| 60 | + for (int i = 0; i < n; ++i) { |
| 61 | + int complement = ((1 << max_bit) - 1) ^ a[i]; |
| 62 | + int current = 0; |
| 63 | + for (int k = max_bit; k--;) { |
| 64 | + if (complement >> k & 1) { |
| 65 | + int t = current | (1 << k); |
| 66 | + if (g[t] > i) |
| 67 | + current = t; |
| 68 | + } |
| 69 | + } |
| 70 | + ans = std::max(ans, a[i] | current); |
| 71 | + } |
| 72 | + std::cout << ans << "\n"; |
| 73 | +} |
| 74 | + |
| 75 | +``` |
| 76 | +
|
| 77 | +# [PE 987 Straight Eight](https://projecteuler.net/problem=987) |
| 78 | +
|
| 79 | +Spent a whole morning on this... |
| 80 | +
|
| 81 | +In a deck of poker cards, we have $14$ ranks and $4$ suits. By Pigeonhole, cards with rank $5$ or $10$ are bound to be used up. Let us use them as "anchors", meaning every straight corresponds to one of the $8$ cards with rank $5$ or $10$. Let $(f_1, \dots, f_8)$ denote the eight straights. |
| 82 | +
|
| 83 | +Let us use the Inclusion-Exclusion principle. We only need to appoint some of the straights to be straight flushes, and compute the number of ways to arrange the rest straights. |
| 84 | +
|
| 85 | +One observation is, if a straight is anchored by a $5$, there are at maximum $5$ different **shapes** of it, namely $(1,2,3,4,5),(2,3,4,5,6),\dots(5,6,7,8,9)$ and so holds true for a straight anchored by a $10$. |
| 86 | +
|
| 87 | +We enumerate the **shapes** of all $8$ straights, there are $5^8$ combinations. |
| 88 | +
|
| 89 | +```python |
| 90 | +def permutate(n, k): |
| 91 | + res = 1 |
| 92 | + for i in range(k): |
| 93 | + res *= n-i |
| 94 | + return res |
| 95 | +
|
| 96 | +def calc(f, mask): |
| 97 | + # @f = the first rank of a flush |
| 98 | + # Bits in @mask: set if it is a flush |
| 99 | + k = [4]*13 |
| 100 | + c = [0]*13 |
| 101 | + for idx, i in enumerate(f): |
| 102 | + if mask>>idx&1: |
| 103 | + for j in range(i, i+5): |
| 104 | + k[j%13] -= 1 |
| 105 | + else: |
| 106 | + for j in range(i, i+5): |
| 107 | + c[j%13] += 1 |
| 108 | + c[4] = c[9] = k[4] = k[9] = 0 # 4, 9 are taken as "anchors", so will not contribute to combination calculations |
| 109 | + res = 1 |
| 110 | + for i in range(13): |
| 111 | + res *= permutate(k[i], c[i]) |
| 112 | + return res |
| 113 | +
|
| 114 | +def dfs(f): |
| 115 | + if len(f) == 8: |
| 116 | + res = 0 |
| 117 | + for mask in range(2**8): |
| 118 | + for i in range(4): |
| 119 | + if (mask>>i&1) and (mask>>(i+4)&1) and (f[i]+4 >= f[i+4] or (f[i] == 0 and f[i+4] == 9)): # if two flushes overlap |
| 120 | + break |
| 121 | + else: # if for-loop ends naturally |
| 122 | + sgn = -1 if (mask.bit_count())%2 else 1 |
| 123 | + res += calc(f, mask)*sgn |
| 124 | + return res |
| 125 | + else: |
| 126 | + start = 0 |
| 127 | + res = 0 |
| 128 | + if len(f) >= 4: |
| 129 | + start = 5 |
| 130 | + for p in range(start, start+5): |
| 131 | + res += dfs(f+[p]) |
| 132 | + return res |
| 133 | +
|
| 134 | +print(dfs([])) |
| 135 | +``` |
| 136 | + |
0 commit comments