D - Journey to Un'Goro
记\(p_i\)表示前缀\(i\)中\(\mathrm r\)的个数。则题目要求的是\(p_r - p_{l-1}\)为奇数最多有多少对。显然应该越平均越好。
\(p_i\)总共有\(n+1\)个,则奇偶数的数量均不超过\(m = \left\lceil \frac {n + 1}{2}\right\rceil\),答案就是\((n + 1 - m )\times m\)。
因此我们可以按照字典序最小进行搜索,并根据\(m\)的值进行剪枝。
#include <bits/stdc++.h>using namespace std;using i32 = int32_t;
using i64 = long long;
using i128 = __int128;#define int i64using vi = vector<int>;
using pii = pair<int, int>;const int inf = 1e9, INF = 1e18;void test() {for (int _ = 1; _ <= 10; _++) {int n = _;vector<vector<int>> ret;int mx = 0;for (int i = 0; i < (1 << n); i++) {vector<int> bit;for (int j = 0; j < n; j++) {if (i >> j & 1) bit.push_back(1);elsebit.push_back(0);}int ans = 0;for (int j = 0; j < n; j++) {int cnt = 0;for (int k = j; k < n; k++) {cnt += bit[k];if (cnt & 1) ans++;}}if (ans > mx) {mx = ans;ret.clear();}if (ans == mx) ret.push_back(bit);}cout << n << ' ';cout << mx << '\n';sort(ret.begin(), ret.end());for (int j = 0; j < ret.size() && j < 100; j++) {cout << j + 1 << " : ";for (int k = 0; k < n; k++) {if (ret[j][k] == 1) cout << 'r';elsecout << 'b';}cout << '\n';}}
}int cnt = 0, n, m;
vector<char> s;void dfs(int i, int x, int y, int t) { // x 为偶数的个数, y 为奇数的个数if (x > m or y > m) return;if (i == n) {if (x != m and y != m) return;for (auto c: s)cout << c;cout << "\n";if (++cnt == 100) exit(0);return;}s[i] = 'b';dfs(i + 1, x + (t == 0), y + (t == 1), t);s[i] = 'r';dfs(i + 1, x + (t == 1), y + (t == 0), t ^ 1);return;
}i32 main() {ios::sync_with_stdio(false), cin.tie(nullptr);cin >> n;m = (n + 2) / 2;cout << m * (n + 1 - m) << "\n";s.resize(n);dfs(0, 1, 0, 0);return 0;
}
F - Kobolds and Catacombs
我们把原序列称为\(a\),排序后称为\(b\)。如果序列\(a,b\)在区间\([l,r]\)中所有数字出现次数相同,则\([l,r]\)可以被分为一段。
根据这个规则,直接贪心分割就好了。
#include <bits/stdc++.h>using namespace std;using i32 = int32_t;
using i64 = long long;
using i128 = __int128;#define int i64using vi = vector<int>;
using pii = pair<int, int>;const int inf = 1e9, INF = 1e18;i32 main() {ios::sync_with_stdio(false), cin.tie(nullptr);int n;cin >> n;vi a(n);for (auto &i: a) cin >> i;vi b = a;ranges::sort(b);int res = 0;unordered_map<int, int> cnt;int x = 0, y = 0;for (int i = 0; i < n; i++) {cnt[a[i]]++;if (cnt[a[i]] == 0) y--;if (cnt[a[i]] == 1) x++;cnt[b[i]]--;if (cnt[b[i]] == 0) x--;if (cnt[b[i]] == -1) y++;if (x == 0 and y == 0) res++;}cout << res;return 0;
}
G - The Witchwood
#include <bits/stdc++.h>using namespace std;using i32 = int32_t;
using i64 = long long;
using i128 = __int128;#define int i64using vi = vector<int>;
using pii = pair<int, int>;const int inf = 1e9, INF = 1e18;i32 main() {ios::sync_with_stdio(false), cin.tie(nullptr);int n, k;cin >> n >> k;vi a(n);for (auto &i: a) cin >> i;ranges::sort(a, greater<>());a.resize(k);i64 sum = 0;for (auto i: a)sum += i;cout << sum;return 0;
}
H - The Boomsday Project
我们考虑记录\(\sum p_i \times q_i\)次骑车。
比如3条记录
1 2
2 3
3 2
我们就要记录为
1 1 2 2 2 3 3
这样的,假设总共骑车\(N\)次,其中第\(i\)次骑车的日期为\(date_i\)。
这样的话,我们记状态\(f[i]\)表示前\(i\)次骑车的最小花费。
我们的转移可以分为两类,第一类是直接购买,\(f[i] = f[i-1] +r\)。
还有一种情况是,我们枚举打折卡\((d,k,c)\),我们找到最早的骑车\(j\),满足\(date_j \ge date_i - d + 1 \and j \ge i + 1 - k\),则有转移\(f[i] = f[j - 1] + c\)。
当然了这样转移复杂度是\(O(N^2 n)\)。
但是我们考虑对于\(i\),\(j\)一定是单调的。这样的话,我们可以用\(n\)个双指针来维护。
#include <bits/stdc++.h>using namespace std;using i32 = int32_t;
using i64 = long long;
using i128 = __int128;#define int i64using vi = vector<int>;
using pii = pair<int, int>;const int inf = 1e9, INF = 1e18;i32 main() {ios::sync_with_stdio(false), cin.tie(nullptr);int n, m, r;cin >> n >> m >> r;vector<array<int, 3>> a(n); // d day, k free rents, c cost;for (auto &[d, k, c]: a) cin >> d >> k >> c;vi date(1);for (int i = 1, p, q; i <= m; i++) {// p date, q = times;cin >> p >> q;while (q--) date.push_back(p);}ranges::sort(date);int N = date.size() - 1;vi f(N + 1), lst(n, 1);for (int i = 1; i <= N; i++) {f[i] = f[i - 1] + r;for (int j = 0; j < n; j++) {const auto &[d, k, c] = a[j];while (date[lst[j]] < date[i] - d + 1 or lst[j] < i + 1 - k)lst[j]++;f[i] = min(f[i], f[lst[j] - 1] + c);}}cout << f[N] << "\n";return 0;
}
I - Rise of Shadows
首先分针的转速\(w_1 = \frac{2\pi}{M}\),时针的转速\(w_2 = \frac{2\pi}{HM}\),在经过\(T\)分钟后的角度差为
根据题目要求得到不等式
和不等式
令\(d = \gcd(H-1,HM)\),根据
可以得到
对于左侧,因为\(\frac{H-1}{d},\frac{HM}{d}\)互质,因此左侧整个的取值为\([0,\frac{HM}{d})\)
对于右侧,根据题目已知条件\(A\le \frac{HM}{2}\)可以得到
因此对于第一个不等式左侧的合法取值范围是\([0,\left\lfloor \frac{A}{d}\right\rfloor]\),第二个不等式合法取值范围是\([\left\lceil \frac{HM - A}{d}\right\rceil,\frac{HM}{d})\)。
所以解的个数就是
特别的当\(A == HM - A\)在等号处会计算重复,此时的解的个数为\(\frac{HM}{d}\)。
当然了以上的计算,可以认为是一轮。一共进行的\(d\)轮,所以最终的答案还要乘\(d\)
#include <bits/stdc++.h>using namespace std;using i32 = int32_t;
using i64 = long long;#define int i64using vi = vector<int>;
using pii = pair<int, int>;
const int inf = LLONG_MAX / 2;i32 main() {int H, M, A;cin >> H >> M >> A;int HM = H * M;int d = gcd(H - 1, HM);if (HM == A * 2) {cout << HM;} else {cout << (A / d + 1 + HM / d - (HM - A + d - 1) / d) * d;}return 0;
}
K - Scholomance Academy
关于题目难点是理解题目。
记真的阳性数为\(Positive\),阴性数为\(Negative\),则有\(Positive=TP + FN,Negative = TN + FP\)。
这样的话\(TPR,FPR\)实际上都是只有一个变量。然后再看这个积分,实际上就是样例里面的图的面积。
这个图怎么来的?实际上就是根据坐标点\((TPR,FPR)\)连成一个折线图。
而积分的值就是\(\sum \frac{tp}{Positive} \times \frac{1}{Negative}\)。
然后我们考虑初始的\(\theta = 0\),此时\(TPR = 1 , FPR = 0\)。然后把所有的\(instances\)按照\(score\)排序,然后初始的\(tp = Positive\),然后如果这个\(type\)为阳性,这说明随着\(\theta\)增大这个阳性要被错误的估计,所以tp —
。否则的话,说明\(FPR\)会发生改变,此时产生了新的值,因此我们要记录一下当前的答案\(\frac{tp}{Positive} \times \frac{1}{Negative}\)。
#include <bits/stdc++.h>using namespace std;using i32 = int32_t;
using i64 = long long;
using i128 = __int128;#define int i64using vi = vector<int>;
using pii = pair<int, int>;const int inf = 1e9, INF = 1e18;struct instance {bool type;int score;bool operator<(instance &b) const {if (score != b.score) return score < b.score;return type > b.type;}
};i32 main() {ios::sync_with_stdio(false), cin.tie(nullptr);int n, positive = 0, negative = 0;cin >> n;vector<instance> a(n);for (char c; auto &it: a) {cin >> c >> it.score;it.type = (c == '+');if (it.type) positive++;else negative++;}sort(a.begin(), a.end());int tp = positive, res = 0;for (auto it: a) {if (it.type) tp--;else res += tp;}cout << fixed << setprecision(20) << (long double) res / (long double) (positive * negative);return 0;
}