The 2020 ICPC Asia Shenyang Regional Programming Contest
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 i64
using 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);
else
bit.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';
else
cout << '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 i64
using 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 i64
using 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 i64
using 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 i64
using 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 i64
using 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;
}