牛客周赛 Round 86
A. 小苯跑外卖
点击查看代码
void solve() {
int x, y;
std::cin >> x >> y;
std::cout << (y + x - 1) / x << "\n";
}
B. 小苯的区间删除
任意次操作,把负数都删掉就行。
点击查看代码
void solve() {
int n, k;
std::cin >> n >> k;
std::vector<int> a(n);
for (int i = 0; i < n; ++ i) {
std::cin >> a[i];
}
i64 ans = 0;
for (int i = 0; i < n; ++ i) {
ans += a[i] > 0 ? a[i] : 0;
}
std::cout << ans << "\n";
}
C. 小苯的数字消除
题意:给你一个\(01\)串,你每次可以选择两个相邻且相等的数同时删去。你可以更改任意位置,求使得最后串剩下的数小于等于1个的最小更改数。
先用栈模拟一遍,类似括号匹配,每次遇到相同的消去。
最后剩下的是一个01相间的串,形如\(010101..\)这种。这个串需要\(\frac{len}{2}\)次操作。
点击查看代码
void solve() {
int n;
std::cin >> n;
std::string s;
std::cin >> s;
std::stack<char> stk;
for (int i = 0; i < n; ++ i) {
if (stk.size() && stk.top() == s[i]) {
stk.pop();
} else {
stk.push(s[i]);
}
}
int ans = (int)stk.size() / 2;
std::cout << ans << "\n";
}
D. 小苯的数字集合
题意:有一个可重集合。一开始有两个数\(x, y\)。你每次可以选择集合的两个数,然后把它们的与值或者或值或者异或值或者\(gcd\)插入集合。求使得集合出现\(0\)的最少操作数。
首先我们最多需要三次操作,因为\(a \oplus b = c, c \oplus b = a, a \oplus a = 0\)。
那么我们分类讨论,一次操作的情况只有与操作和异或操作可以实现。
然后是两次操作的情况,我们把每个操作的值都插入,然后枚举它和\(x, y\)进行的操作看能不能得到\(0\)。
否则答案就是3。
点击查看代码
void solve() {
int x, y;
std::cin >> x >> y;
//x^y=z
//z^y=x
//x^x=0
if ((x & y) == 0 || (x ^ y) == 0) {
std::cout << 1 << "\n";
} else {
std::vector<int> a{x & y, x | y, x ^ y, std::gcd(x, y)};
for (auto & z : a) {
if ((x & z) == 0 || (x | z) == 0 || (x ^ z) == 0 || (y & z) == 0 || (y | z) == 0 || (y ^ z) == 0) {
std::cout << 2 << "\n";
return;
}
}
std::cout << 3 << "\n";
}
}
E. 小苯的Polygon
题意:给你\(n\)个木棍,每个木棍有长度,你要用他们构造一个凸多边形,使得这个凸多边形的周长最小。
凸多边形的任意一条边长度都比其它\(n-1\)条边的长度之和小。
那么我们枚举最长的一条边,然后把比它小的边进行背包\(dp\)求出可以凑出的长度,取最接近它的。
点击查看代码
void solve() {
int n;
std::cin >> n;
std::vector<int> a(n);
for (int i = 0; i < n; ++ i) {
std::cin >> a[i];
}
if (n < 3) {
std::cout << -1 << "\n";
return;
} else if (n == 3) {
if (a[0] + a[1] <= a[2] || a[0] + a[2] <= a[1] || a[1] + a[2] <= a[0]) {
std::cout << -1 << "\n";
} else {
std::cout << a[0] + a[1] + a[2] << "\n";
}
} else {
std::ranges::sort(a);
int ans = -1;
std::bitset<10010> b;
b[0] = 1;
for (int i = 0; i < n; ++ i) {
if (i >= 2) {
for (int j = a[i] + 1; j <= 10000; ++ j) {
if (b[j]) {
if (ans == -1 || a[i] + j < ans) {
ans = a[i] + j;
}
break;
}
}
}
b |= b << a[i];
}
std::cout << ans << "\n";
}
}
F. 小苯的线性dp
题意:给你一个数组,每次你可以进行一次合并操作,把两个数更换为它们的和。求分别在\([0, n - 1]\)操作后数组的最大极差。
首先我们想,合并的这些区间应该尽可能是一个区间,因为我们要让最大值最大,所以应该把数都合并到一起,不过也会出现操作两个区间的情况,这个下面会说。
我们可以用\(st\)表维护区间最大最小值,枚举操作的区间,那么假设这个区间的长度为\(len\),则\(ans[len - 1] = \min(ans[len - 1], \max(sum[i..j], left_{max}, right_{max}) - \min(left_{min}, right_{min})\)。
然后我们考虑操作了两个区间的情况,这种情况就是最小值和其它值相差太大,导致我们不能合并这个最小值,于是只能跨过它,进行一些无效操作。那么这意味着\(ans[i] = \max(ans[0], ans[1], .., ans[i - 1], ans[i])\)。但注意最后如果合并到只剩三个数,就是中间是最小值的情况,我们还要进行操作就只能合并它,所以\(ans[n - 2]\)不能和前面的取最大值。
不过\(ac\)后面发现其实我们求的是前后缀的最大最小值,似乎不需要\(st\)。
点击查看代码
const i64 inf = 1e18;
template <class Info>
struct ST {
std::vector<std::vector<Info>> st;
ST(std::vector<Info> a) {
int n = a.size(), m = std::__lg(n) + 1;
st.assign(n, std::vector<Info>(m));
for (int i = 0; i < n; ++ i) {
st[i][0] = a[i];
}
for (int j = 1; j < m; ++ j) {
for (int i = 0; i + (1 << j - 1) < n; ++ i) {
st[i][j] = st[i][j - 1] + st[i + (1 << j - 1)][j - 1];
}
}
}
Info query(int l, int r) {
if (l > r) {
return {-inf, inf};
}
int lg = std::__lg(r - l + 1);
return st[l][lg] + st[r - (1 << lg) + 1][lg];
}
};
struct Info {
i64 max, min;
};
Info operator + (const Info & a, const Info & b) {
Info res{};
res.max = std::max(a.max, b.max);
res.min = std::min(a.min, b.min);
return res;
}
void solve() {
int n;
std::cin >> n;
std::vector<int> a(n);
std::vector<Info> info(n + 1);
for (int i = 0; i < n; ++ i) {
std::cin >> a[i];
info[i + 1] = {a[i], a[i]};
}
ST<Info> st(info);
std::vector<i64> sum(n + 1);
for (int i = 1; i <= n; ++ i) {
sum[i] = sum[i - 1] + a[i - 1];
}
std::vector<i64> ans(n);
ans[0] = st.query(1, n).max - st.query(1, n).min;
for (int i = 1; i <= n; ++ i) {
for (int j = i + 1; j <= n; ++ j) {
auto l = st.query(1, i - 1);
auto r = st.query(j + 1, n);
ans[j - i] = std::max(ans[j - i], std::max({sum[j] - sum[i - 1], l.max, r.max}) - std::min(l.min, r.min));
}
}
for (int i = 1; i + 2 < n; ++ i) {
ans[i] = std::max(ans[i], ans[i - 1]);
}
for (int i = 0; i < n; ++ i) {
std::cout << ans[i] << " \n"[i == n - 1];
}
}