Codeforces Global Round 16 题解
旅行传送门
A. Median Maximization
题意:给你两个正整数 \(n\) 和 \(s\) 。求由 \(n\) 个非负整数组成的总和为 \(s\) 的序列的最大中位数。
题目分析:显然,令前 $ \frac{n+1}{2}-1$ 个数为 \(0\) 时序列有最大中位数。
AC代码:
#include <bits/stdc++.h>
#define rep(i, x, y) for (register int i = (x); i <= (y); i++)
#define down(i, x, y) for (register int i = (x); i >= (y); i--)
using ll = long long;
char buf[1 << 23], *p1 = buf, *p2 = buf, obuf[1 << 23], *O = obuf;
#define getchar() (p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1 << 21, stdin), p1 == p2) ? EOF : *p1++)
inline ll read()
{
ll x = 0, f = 1;
char ch = getchar();
while (!isdigit(ch))
{
if (ch == '-')
f = -1;
ch = getchar();
}
while (isdigit(ch))
{
x = x * 10 + ch - '0';
ch = getchar();
}
return x * f;
}
ll solve()
{
ll n = read(), s = read();
return n & 1 ? s / ceil(1.0 * n / 2) : s / (ceil(1.0 * n / 2) + 1);
}
int main(int argc, char const *argv[])
{
int T = read();
while (T--)
printf("%lld\n", solve());
return 0;
}
B. MIN-MEX Cut
题意:设二进制字符串的 \(MEX\) 为字符串中未出现的 \(0\) 、 \(1\) 或 \(2\) 中最小的数字。
现在给你一个字符串 \(s\) ,你可以将 \(s\) 切成任意几份(分成几个子串),求所有子串片段的最小 \(MEX\) 总和。
题目分析:要使得 \(MEX\) 总和最小,显然要将连续的 \(0\) 切分出来。另外要明确的是 \(ans \leq 2\) ,因此比较连续段 \(0\) 的段数和 \(2\) 的大小即可。
AC代码:
#include <bits/stdc++.h>
#define rep(i, x, y) for (register int i = (x); i <= (y); i++)
#define down(i, x, y) for (register int i = (x); i >= (y); i--)
#define IOS \
ios::sync_with_stdio(false); \
cin.tie(nullptr); \
cout.tie(nullptr);
using namespace std;
int solve()
{
string s;
cin >> s;
int len = s.length();
int i = 0, res = 0;
while (i < len)
{
while (s[i] - '0' == 0)
{
++i;
if (s[i] - '0' == 1)
{
++res;
break;
}
}
++i;
}
if (s[len - 1] - '0' == 0)
++res;
return std::min(res, 2);
}
int main(int argc, char const *argv[])
{
int T;
cin >> T;
while (T--)
printf("%d\n", solve());
return 0;
}
C. MAX-MEX Cut
题意:给你一个 \(2 \times n\) 的 \(01\) 矩阵, 你可以将该矩阵切成任意几份(分成几个 \(2 \times m\) 的子矩阵),求所有子矩阵的最大 \(MEX\) 总和。
题目分析:我们不妨枚举下 \(2 \times 2\) 的 \(01\) 矩阵的所有情况,\({\begin{bmatrix}1&0\\0&1\end{bmatrix}}\) 、 \({\begin{bmatrix}1&0\\0&0\end{bmatrix}}\) 、 \({\begin{bmatrix}1&1\\0&1\end{bmatrix}}\) 、 \({\begin{bmatrix}0&0\\1&0\end{bmatrix}}\) 、 \({\begin{bmatrix}0&1\\1&1\end{bmatrix}}\) 、 \({\begin{bmatrix}0&1\\0&1\end{bmatrix}}\) ,不难发现,\({\begin{bmatrix}1\\1\end{bmatrix}}\) 本身对答案是没有贡献的,但它与 \({\begin{bmatrix}0\\0\end{bmatrix}}\) 组合时,却能使 \({\begin{bmatrix}0\\0\end{bmatrix}}\) 的贡献 \(+1\) ,而 \({\begin{bmatrix}1\\0\end{bmatrix}}\) (\({\begin{bmatrix}0\\1\end{bmatrix}}\))的贡献本身已经是 \(2\) 了,与其它的 \(1 \times 2\) 矩阵组合时也不会产生多的贡献。那么我们的思路已经很明确了,先找出主矩阵中的所有 \({\begin{bmatrix}1\\1\end{bmatrix}}\) 子矩阵,与它旁边空闲的 \({\begin{bmatrix}0\\0\end{bmatrix}}\) 组合,最后再扫一遍将未组合的 \(1 \times 2\) 矩阵切分出来加上贡献即是最终答案。
AC代码:
#include <bits/stdc++.h>
#define rep(i, x, y) for (register int i = (x); i <= (y); i++)
#define down(i, x, y) for (register int i = (x); i >= (y); i--)
#define IOS \
ios::sync_with_stdio(false); \
cin.tie(nullptr); \
cout.tie(nullptr);
using namespace std;
int solve()
{
int n, res = 0;
string s1, s2;
cin >> n >> s1 >> s2;
std::vector<int> vis(n);
rep(i, 0, n - 1)
{
if (s1[i] == '1' && s2[i] == '1')
if (i > 0 && s1[i - 1] == '0' && s2[i - 1] == '0' && !vis[i - 1] && !vis[i])
res += 2, vis[i - 1] = vis[i] = 1;
else if (i < n && s1[i + 1] == '0' && s2[i + 1] == '0' && !vis[i] && !vis[i + 1])
res += 2, vis[i] = vis[i + 1] = 1;
}
rep(i, 0, n - 1)
{
if (s1[i] != s2[i])
res += 2, vis[i] = 1;
else if (s1[i] == '0' && s2[i] == '0' && !vis[i])
++res, vis[i] = 1;
}
return res;
}
int main(int argc, char const *argv[])
{
int T;
cin >> T;
while (T--)
printf("%d\n", solve());
return 0;
}
D1. Seating Arrangements (easy version)
题意:有 \(n \times m\) 个人和 \(n \times m\) 个座位,座位编号如题面所示。每个人的视力对应一个值 \(a_i\) ,\(a_i\)越小的人座位越靠前。从第 \(1\) 个人至 第 \(n \times m\) 个人依次就座,每个人就座时的代价为该行经过的人数,求如何安排座位可使代价最小化。
题目分析: 显然,对某一行来说,若同一个数值连续出现,那么他们之间不会产生任何代价(把编号较小的放进靠后的位置)。此外,数值大的在数值小的之前出现,也不会产生任何代价(数值大的先坐进靠后的位置去了)。那么会对答案产生贡献的不就一种情况了吗?数值小的在数值大的之前!这是什么?正序对?树状数组走起。
需要注意: \(a_i\)很大,离散一下。
AC代码:
#include <bits/stdc++.h>
#define rep(i, x, y) for (register int i = (x); i <= (y); i++)
#define down(i, x, y) for (register int i = (x); i >= (y); i--)
#define full(x, y) memset(x, y, sizeof(x))
#define lowbit(x) ((x) & (-x))
const int maxn = 305;
char buf[1 << 23], *p1 = buf, *p2 = buf, obuf[1 << 23], *O = obuf;
#define getchar() (p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1 << 21, stdin), p1 == p2) ? EOF : *p1++)
inline int read()
{
int x = 0, f = 1;
char ch = getchar();
while (!isdigit(ch))
{
if (ch == '-')
f = -1;
ch = getchar();
}
while (isdigit(ch))
{
x = x * 10 + ch - '0';
ch = getchar();
}
return x * f;
}
int n, m, a[maxn], b[maxn], tree[maxn];
void add(int x, int k)
{
for (; x <= m; x += lowbit(x))
tree[x] += k;
}
int ask(int x)
{
int res = 0;
for (; x; x -= lowbit(x))
res += tree[x];
return res;
}
int solve()
{
full(tree, 0);
int res = 0;
n = read(), m = read();
rep(i, 1, m) a[i] = b[i] = read();
std::sort(b + 1, b + m + 1);
int nn = std::unique(b + 1, b + m + 1) - b - 1;
rep(i, 1, m) a[i] = std::lower_bound(b + 1, b + nn + 1, a[i]) - b;
rep(i, 1, m) add(a[i], 1), res += ask(a[i] - 1);
return res;
}
int main(int argc, char const *argv[])
{
int T = read();
while (T--)
printf("%d\n", solve());
return 0;
}
D2. Seating Arrangements (hard version)
题目分析:与 \(easy\) \(version\) 不同的是现在有多行了,基本思路还是树状数组求正序对,一开始想的是先按数值排个序,然后按顺序每行选出 \(m\) 个人,再对这 \(m\) 个人按编号排序,用D1的做法求他们对答案的贡献,可惜超时了 ┑(﹀_﹀)┍
后来去luogu逛了一圈发现有julao也用的是树状数组的思路,但julao的处理方式明显比我更优,这里开个旅行传送门,在此不过多赘述了。
AC代码:
#include <bits/stdc++.h>
#define rep(i, x, y) for (register int i = (x); i <= (y); i++)
#define down(i, x, y) for (register int i = (x); i >= (y); i--)
#define pii std::pair<int, int>
#define full(x, y) memset(x, y, sizeof(x))
#define lowbit(x) ((x) & (-x))
const int maxn = 1e5 + 5;
char buf[1 << 23], *p1 = buf, *p2 = buf, obuf[1 << 23], *O = obuf;
#define getchar() (p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1 << 21, stdin), p1 == p2) ? EOF : *p1++)
inline int read()
{
int x = 0, f = 1;
char ch = getchar();
while (!isdigit(ch))
{
if (ch == '-')
f = -1;
ch = getchar();
}
while (isdigit(ch))
{
x = x * 10 + ch - '0';
ch = getchar();
}
return x * f;
}
int n, m, cnt;
pii a[maxn], b[maxn];
int c[maxn], tree[305][maxn];
std::map<int, int> mp;
void add(int x, int y, int k)
{
for (; x <= cnt; x += lowbit(x))
tree[y][x] += k;
}
int ask(int x, int y)
{
int res = 0;
for (; x; x -= lowbit(x))
res += tree[y][x];
return res;
}
inline bool cmp(pii x, pii y) { return x.second ^ y.second ? x.second < y.second : x.first < y.first; }
int solve()
{
int res = 0;
n = read(), m = read();
rep(i, 1, n * m) a[i].second = b[i].second = c[i] = read(), b[i].first = i;
std::sort(b + 1, b + n * m + 1, cmp);
rep(i, 1, n * m) a[b[i].first].first = i;
std::sort(c + 1, c + n * m + 1);
cnt = std::unique(c + 1, c + n * m + 1) - c - 1;
rep(i, 1, n * m) a[i].second = std::lower_bound(c + 1, c + cnt + 1, a[i].second) - c;
rep(i, 1, n) rep(j, 1, cnt) tree[i][j] = 0;
rep(i, 1, n * m)
{
int id = (a[i].first - 1) / m + 1;
res += ask(a[i].second - 1, id);
add(a[i].second, id, 1);
}
return res;
}
int main(int argc, char const *argv[])
{
int T = read();
while (T--)
printf("%d\n", solve());
return 0;
}
E. Buds Re-hanging
题意:给你一棵树,树的芽满足以下条件:
- 非根非叶子节点
- 所有子节点均为叶子节点
你可以剪掉一棵芽(删去芽与其父节点的连边)并放在其他任意节点上,该操作可以进行任意次,现在要你最小化叶子节点数目,并求最小值是多少。
题目分析:结论题,首先将整棵树尽可能多地拆成芽并放在根节点上形成一棵深度 \(\leq 3\) 的树,因为每个带有 \(x\) 个叶子节点的芽在嫁接到另一节点上后,对答案的贡献是 \(x-1\) ,所以保持其中一个芽不动,其它芽顺着它连成一条链即是最优解。
图是从luogu偷的,但画的着实好,实在忍不住。
为了观感更好,我在原图连成链的时候把嫁接的地方换成虚线了。
AC代码:
#include <bits/stdc++.h>
#define rep(i, x, y) for (register int i = (x); i <= (y); i++)
#define down(i, x, y) for (register int i = (x); i >= (y); i--)
const int maxn = 2e5 + 5;
char buf[1 << 23], *p1 = buf, *p2 = buf, obuf[1 << 23], *O = obuf;
#define getchar() (p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1 << 21, stdin), p1 == p2) ? EOF : *p1++)
inline int read()
{
int x = 0, f = 1;
char ch = getchar();
while (!isdigit(ch))
{
if (ch == '-')
f = -1;
ch = getchar();
}
while (isdigit(ch))
{
x = x * 10 + ch - '0';
ch = getchar();
}
return x * f;
}
int n, ans;
std::vector<int> e[maxn];
void init()
{
//其中一个芽不动,其对答案的贡献就为其叶节点数x而非x-1
ans = 1;
rep(i, 1, n) e[i].clear();
}
int dfs(int u, int f)
{
int cnt = 0;
for (auto v : e[u])
{
if (v == f)
continue;
cnt += dfs(v, u);
}
if (cnt)
ans += cnt - 1;
return cnt ? 0 : 1;
}
int main(int argc, char const *argv[])
{
int T = read();
while (T--)
{
n = read();
init();
rep(i, 1, n - 1)
{
int u = read(), v = read();
e[u].push_back(v);
e[v].push_back(u);
}
dfs(1, 0);
printf("%d\n", ans);
}
return 0;
}