小白月赛 Round 132 题解
A 出题需要 rating
知识点:模拟
模拟一下分数变化即可。
时间复杂度 。
void solve() {
int n, c = 1000;
cin >> n;
vector<int> s(7);
for (int i = 0, x; i < n; ++i) {
cin >> x;
c += x;
if (c < 700)
s[0]++;
else if (c < 1100)
s[1]++;
else if (c < 1500)
s[2]++;
else if (c < 2000)
s[3]++;
else if (c < 2400)
s[4]++;
else if (c < 2800)
s[5]++;
else
s[6]++;
}
for (int i = 0; i < 7; ++i) cout << s[i] << " \n"[i == 6];
}
B 出题需要语文
知识点:贪心、模拟
每套题必须包含 到
各一道,因此每个难度之间互不影响。为了让平均质量尽可能高,对于每个难度,只需要选择质量分不低于
的题中质量最高的一道。
设最终选出的 道题质量和为
,平均质量不低于
等价于
。如果某个难度没有可选题,或者每个难度都选最优后仍然
,则无解;否则输出这
道题的编号即可。
时间复杂度 。
void solve() {
int n;
cin >> n;
vector<PII> a(6);
for (int i = 1; i <= n; ++i) {
char c;
int x;
cin >> c >> x;
if (x >= 60 && x > a[c - 'A'].fi) a[c - 'A'] = {x, i};
}
int sum = 0;
for (auto [u, v] : a) {
if (!u) {
cout << -1 << endl;
return;
}
sum += u;
}
if (sum < 420) {
cout << -1 << endl;
return;
}
for (auto [u, v] : a) cout << v << " ";
cout << endl;
}
C 出题需要加法
知识点:二进制、前缀计数
可合数形如 。由于二进制表示唯一,当
时结果的二进制中恰好有两个
;当
时结果为
,也对应唯一的一种合成方式。因此只需枚举所有
,统计不超过某个上界的可合数个数。
记 表示
中可合数的数量,则答案为:
枚举时令 ,避免重复统计同一个数,枚举到
即可。
时间复杂度
void solve() {
int l, r;
cin >> l >> r;
auto calc = [&](int n) {
int res = 0;
for (int i = 0; i <= 62; ++i) {
for (int j = 0; j <= i; ++j) {
int x = (1LL << i) + (1LL << j);
if (x <= n) ++res;
}
}
return res;
};
cout << calc(r) - calc(l - 1) << endl;
}
当然也可以预处理所有可合数,然后二分查找。
时间复杂度
vector<int> vals;
void init() {
for (int i = 0; i <= 62; ++i) {
for (int j = 0; j <= i; ++j) {
int val = (1LL << i) + (1LL << j);
vals.pb(val);
}
}
sort(vals);
}
void solve() {
int l, r;
cin >> l >> r;
auto calc = [&](int n) { return upper_bound(all(vals), n) - vals.begin(); };
cout << calc(r) - calc(l - 1) << endl;
}
D 出题需要构造
知识点:构造、循环移位
一次操作选出 个格子的值,实际会把这些值在矩阵中出现的所有位置都放入小球。因此我们只需要构造一个矩阵,使得选定的若干个数字出现的位置在每行、每列中都恰好有
个。
若 ,总小球数从行看为
,从列看为
,必须相等,因此无解。若
,一行一列不可能都有
个小球,也无解。
当 时,直接全填
,选择数字
,每行每列都有
个。
当 时,构造循环矩阵:
每个数字在每行、每列中都恰好出现一次,因此选择任意两个不同数字即可让每行、每列恰好有 个小球。
时间复杂度 。
void solve() {
int n, m;
cin >> n >> m;
if (n != m || n < 2) {
cout << "NO" << endl;
return;
}
cout << "YES" << endl;
cout << 2 << endl;
for (int i = 0; i < n; ++i) {
for (int j = 0; j < n; ++j) {
cout << (i + j) % n + 1 << " \n"[j == n - 1];
}
}
}
E 出题需要魔法
知识点:单调栈、补集计数
考虑固定位置 。在一个包含
的子区间
中,
可见当且仅当:
- 左侧
中没有比
大的数;
- 或右侧
中没有比
大的数。
反过来, 不可见,当且仅当左右两边都存在比
大的数。
设:
表示
左边最近的、满足
的位置,若不存在则为
;
表示
右边最近的、满足
的位置,若不存在则为
。
包含位置 的子区间总数为:
若 不可见,则区间必须满足
且
,这样的区间数量为:
因此答案为:
可以用单调栈分别从左到右、从右到左求出。
时间复杂度 。
void solve() {
int n;
cin >> n;
vector<int> p(n + 1), L(n + 1), R(n + 1);
for (int i = 1; i <= n; ++i) cin >> p[i];
vector<int> st;
for (int i = 1; i <= n; ++i) {
while (!st.empty() && p[st.back()] < p[i]) st.pob();
L[i] = st.empty() ? 0 : st.back();
st.pb(i);
}
st.clear();
for (int i = n; i >= 1; --i) {
while (!st.empty() && p[st.back()] < p[i]) st.pob();
R[i] = st.empty() ? n + 1 : st.back();
st.pb(i);
}
for (int i = 1; i <= n; ++i) cout << i * (n - i + 1) - L[i] * (n - R[i] + 1) << " \n"[i == n];
}
F 出题需要树论
知识点:树形 、
、前后缀最大值
考虑选择以节点 为根的子树。对于不在该子树内的叶子,它到根的路径和不变;对于在该子树内的叶子,只有从
到该叶子的这一段权值会被异或上
,而
的父亲到根这一段保持不变。
先以 为根遍历整棵树,求出每个叶子原本的根到叶路径和。一个节点子树内的所有叶子在这个顺序中是连续的一段,记为
。
再自底向上求:
也就是如果选择 的子树,子树内部到叶子的最大贡献。
对于每个节点 :
- 子树外叶子的最大原路径和,可以用叶子路径和的前缀最大值、后缀最大值求出;
- 子树内叶子的最大新路径和为:
其中 是根到
父亲的原路径和,若
则为
。
于是选择 的答案为两者最大值,对所有
取最小即可。
时间复杂度 。
void solve() {
int n, x;
cin >> n >> x;
vector<int> a(n + 1);
for (int i = 1; i <= n; ++i) cin >> a[i];
vector<vector<int>> g(n + 1);
for (int i = 1; i < n; ++i) {
int u, v;
cin >> u >> v;
g[u].pb(v), g[v].pb(u);
}
vector<int> pa(n + 1), order, pre(n + 1), st;
st.pb(1);
pre[1] = a[1];
while (st.size()) {
auto u = st.back();
st.pob();
order.pb(u);
for (auto v : g[u]) {
if (v == pa[u]) continue;
pa[v] = u;
pre[v] = pre[u] + a[v];
st.pb(v);
}
}
vector<bool> leaf(n + 1);
for (int u = 1; u <= n; ++u) leaf[u] = (n == 1 || (u != 1 && g[u].size() == 1));
vector<int> id(n + 1, -1), val;
for (auto u : order) {
if (leaf[u]) {
id[u] = val.size() + 1;
val.pb(pre[u]);
}
}
vector<int> down(n + 1), L(n + 1), R(n + 1);
for (int i = order.size() - 1; i >= 0; --i) {
int u = order[i];
if (leaf[u]) {
L[u] = R[u] = id[u];
down[u] = (a[u] ^ x);
} else {
int l = n, r = -1;
int mx = -INF;
for (int v : g[u]) {
if (v == pa[u]) continue;
l = min(l, L[v]);
r = max(r, R[v]);
mx = max(mx, down[v]);
}
L[u] = l;
R[u] = r;
down[u] = (a[u] ^ x) + mx;
}
}
int m = val.size();
vector<int> premx(m + 1, -INF), sufmx(m + 2, -INF);
for (int i = 1; i <= m; ++i) premx[i] = premx[i] = max(premx[i - 1], val[i - 1]);
for (int i = m; i >= 1; --i) sufmx[i] = max(sufmx[i + 1], val[i - 1]);
int ans = -INF;
for (int u = 1; u <= n; ++u) {
int cur = max(max(premx[L[u] - 1], sufmx[R[u] + 1]), (u == 1 ? 0 : pre[pa[u]]) + down[u]);
ans = (u == 1 ? cur : min(ans, cur));
}
cout << ans << endl;
}

京公网安备 11010502036488号