A. Tetris
找每一列的最小值。
点击查看代码
void solve() {int n, m;std::cin >> n >> m;std::vector<int> cnt(n);for (int i = 0; i < m; ++ i) {int x;std::cin >> x;++ cnt[x - 1];}int ans = m;for (int i = 0; i < n; ++ i) {ans = std::min(ans, cnt[i]);}std::cout << ans << "\n";
}
B. Lecture Sleep
前缀和加枚举。
点击查看代码
void solve() {int n, k;std::cin >> n >> k;std::vector<int> a(n), b(n);for (int i = 0; i < n; ++ i) {std::cin >> a[i];}for (int i = 0; i < n; ++ i) {std::cin >> b[i];} std::vector<int> sum(n + 1), pre(n + 1), suf(n + 2);for (int i = 0; i < n; ++ i) {sum[i + 1] = sum[i] + a[i];pre[i + 1] = pre[i];if (b[i]) {pre[i + 1] += a[i];}}for (int i = n - 1; i >= 0; -- i) {suf[i + 1] = suf[i + 2];if (b[i]) {suf[i + 1] += a[i];}}int ans = 0;for (int i = 1; i + k - 1 <= n; ++ i) {ans = std::max(ans, pre[i - 1] + suf[i + k] + sum[i + k - 1] - sum[i - 1]);}std::cout << ans << "\n";
}
C. Chessboard
题意:有\(4\)个\(n\times n\)的\(01\)矩阵,要求修改距离少的数,使得按照某种顺序把它们拼成\(2n\times 2n\)的矩阵后相邻两个数没有相同的。
枚举所有组合情况,然后每个情况找到需要更改的最小值。因为任意两个相邻的都不相等,所以矩阵只有两种形态,比较这两个形态就可以知道需要更改的最小值。
点击查看代码
void solve() {int n;std::cin >> n;std::vector a(4, std::vector<std::string>(n));for (int i = 0; i < 4; ++ i) {for (int j = 0; j < n; ++ j) {std::cin >> a[i][j];}}std::vector<int> p(4);std::ranges::iota(p, 0);auto get = [&]() -> int {std::vector<std::string> s(2 * n);for (int i = 0; i < n; ++ i) {s[i] = a[p[0]][i] + a[p[1]][i];}for (int i = n; i < 2 * n; ++ i) {s[i] = a[p[2]][i - n] + a[p[3]][i - n];}int sum1 = 0, sum2 = 0;for (int i = 0; i < 2 * n; ++ i) {for (int j = 0; j < 2 * n; ++ j) {sum1 += s[i][j] - '0' != (i + j & 1);sum2 += s[i][j] - '0' == (i + j & 1);}}return std::min(sum1, sum2);};int ans = 2 * n * 2 * n;do {ans = std::min(ans, get());} while (std::ranges::next_permutation(p).found);std::cout << ans << "\n";
}
D. Pair Of Lines
题意:给你\(n\)个点,判断有没有两条直线使得任意一个点都至少在一条直线上。
我们随便选两个点,它们不在一条直线的概率最小是\(\frac{1}{2}\)。那么我们可以随机一百次,每次选两个点把它们构成的直线上的点都去掉,然后看剩下的点是不是在一条直线上。
点击查看代码
std::mt19937 gen(std::random_device{}());int rand(int l, int r) {std::uniform_int_distribution<int> dis(l, r);return dis(gen);
}void solve() {int n;std::cin >> n;std::vector<std::pair<i64, i64>> a(n);for (auto & [x, y] : a) {std::cin >> x >> y;}auto check = [&](int p1, int p2) -> bool {std::vector<std::pair<i64, i64>> b;for (int i = 0; i < n; ++ i) {if (i == p1) {continue;}auto & [x1, y1] = a[p1];auto & [x2, y2] = a[p2];auto & [x3, y3] = a[i];//(x1 - x2) / (y1 - y2) == (x1 - x3) / (y1 - y3)if ((x1 - x2) * (y1 - y3) != (x1 - x3) * (y1 - y2)) {b.emplace_back(a[i]);}}for (int i = 1; i + 1 < b.size(); ++ i) {auto & [x1, y1] = b[i - 1];auto & [x2, y2] = b[i];auto & [x3, y3] = b[i + 1];if ((x1 - x2) * (y1 - y3) != (x1 - x3) * (y1 - y2)) {return false;}}return true;};int t = 100;while (t -- ) {int i = rand(0, n);int j = rand(0, n);while (i == j) {j = rand(0, n);}if (check(i, j)) {std::cout << "YES\n";return;}}std::cout << "NO\n";
}
E. Tufurama
题意:给你一个数组,求有多少\(i, j\),满足\(i < j, a_i \geq j, a_j \geq i\)。
对于每个\(i\),我们考虑它和前面的位置有多少合法的。那么前面位置最多到\(\min(i - 1, a_i)\),判断这个前缀大于等于\(j\)的值有多少。
我们可以离散化加树状数组做。
点击查看代码
template <class T>
struct Fenwick {int n;std::vector<T> tr;Fenwick(int _n) {init(_n);}void init(int _n) {n = _n;tr.assign(_n + 1, T{});}void add(int x, const T &v) {for (int i = x; i <= n; i += i & -i) {tr[i] = tr[i] + v;}}T query(int x) {T res{};for (int i = x; i; i -= i & -i) {res = res + tr[i];}return res;}T sum(int l, int r) {return query(r) - query(l - 1);}
};void solve() {int n;std::cin >> n;std::vector<int> a(n);for (int i = 0; i < n; ++ i) {std::cin >> a[i];}auto b = a;for (int i = 1; i <= n; ++ i) {b.push_back(i);}std::sort(b.begin(), b.end());b.erase(std::unique(b.begin(), b.end()), b.end());auto get = [&](int x) -> int {return std::ranges::lower_bound(b, x) - b.begin() + 1;};for (auto & x : a) {x = get(x);}int m = b.size();std::vector<std::vector<int>> Q(n + 1);for (int i = 2; i <= n; ++ i) {int p = std::min(get(i - 1), a[i - 1]);Q[p].push_back(get(i));}i64 ans = 0;Fenwick<int> tr(m + 1);for (int i = 1; i <= n; ++ i) {tr.add(a[i - 1], 1);for (auto & p : Q[i]) {ans += tr.sum(p, m);}}std::cout << ans << "\n";
}
F. k-substrings
题意:给你一个字符串,判断每个\(k \in [1, \lceil \frac{n}{2} \rceil]\)的\(s[k .. n - k + 1]\)这个子串最长的为奇数的\(border\)的长度。
我们从大到小操作,发现每次只会新加两个字符,这意味着\(ans_i \leq ans_{i+1} + 2\)。那么我们用\(hash\)暴力判断就行,因为\(border\)长度最多增加\(\lceil \frac{n}{2} \rceil\)次,那么我们也最多判断\(\lceil \frac{n}{2} \rceil\)次。
这题卡单哈希,需要用双模哈希。
点击查看代码
const int mod1 = 1000000271, mod2 = 1000000447;void solve() {int n;std::cin >> n;std::string s;std::cin >> s;std::vector<int> h1(n + 1), h2(n + 1), p1(n + 1), p2(n + 1);for (int i = 0; i < n; ++ i) {h1[i + 1] = (h1[i] * 131ll + s[i] - 'a' + 1) % mod1;}p1[0] = 1;for (int i = 1; i <= n; ++ i) {p1[i] = p1[i - 1] * 131ll % mod1;}for (int i = 0; i < n; ++ i) {h2[i + 1] = (h2[i] * 131ll + s[i] - 'a' + 1) % mod2;}p2[0] = 1;for (int i = 1; i <= n; ++ i) {p2[i] = p2[i - 1] * 131ll % mod2;}auto get1 = [&](int l, int r) -> int {return (h1[r] - ((i64)h1[l - 1] * p1[r - l + 1]) % mod1 + mod1) % mod1;};auto get2 = [&](int l, int r) -> int {return (h2[r] - ((i64)h2[l - 1] * p2[r - l + 1]) % mod2 + mod2) % mod2;};auto same = [&](int l1, int r1, int l2, int r2) -> bool {return get1(l1, r1) == get1(l2, r2) && get2(l1, r1) == get2(l2, r2);}; std::vector<int> ans((n + 1) / 2 + 1);for (int i = (n + 1) / 2, len = -1; i >= 1; -- i) {len += 2;if (len >= n - i + 1 - i + 1) {len -= 2;}while (len >= 1 && !same(i, i + len - 1, n - i + 1 - len + 1, n - i + 1)) {len -= 2;}ans[i] = len;}for (int i = 1; i <= (n + 1) / 2; ++ i) {std::cout << ans[i] << " \n"[i == (n + 1) / 2];}
}