牛客周赛 Round 70题解
牛客周赛 Round 70题解
A 小苯晨跑
#include <bits/stdc++.h>
using namespace std;
void solve() {
int a[4];
for (int i = 0; i < 4; i ++ ) cin >> a[i];
sort(a, a + 4);
if (a[0] == a[3]) {
cout << "NO\n";
} else {
cout << "YES\n";
}
}
int main() {
int T; cin >> T;
while (T -- ) solve();
return 0;
}
B 小苯过马路
#include <bits/stdc++.h>
using namespace std;
void solve() {
int x, y, k, t;
cin >> x >> y >> k >> t;
char c; cin >> c;
if (c == 'G') {
if (t <= k) cout << t << endl;
else cout << k + x + t << endl;
} else {
cout << k + t << endl;
}
}
int main() {
int T = 1;
while (T -- ) solve();
return 0;
}
C 小苯的字符串染色
由于可以选择长度为1的区间,所以直接把所有单个黑的区间染成白的即可。
#include <bits/stdc++.h>
using namespace std;
void solve() {
int n; cin >> n;
string s; cin >> s;
vector<int> a;
for (int i = 0; i < n; i ++ ) {
if (s[i] == '1') a.push_back(i + 1);
}
cout << a.size() << endl;
for (auto c : a) cout << c << ' ' << c << endl;
}
int main() {
int T = 1;
cin >> T;
while (T -- ) solve();
return 0;
}
D 小苯的能量项链
题意:从左右两边开始去掉珠子,最多可以去k个,问最后剩下珠子中的左右端点权值和最大是多少。
考虑枚举最后剩下的珠子中的其中一个端点,这里以枚举右端点为例。
假如我们最后剩下的右端点是 \(i\) ,表示右边已经去掉了 \(n-i\) 个珠子,那么此时左边最多只能去掉 \(k-(n-i)\) 个珠子,记作 \(d\) 。那么最后的左端点只可能是 \([1, d+1]\) ,我们为了让这次枚举的左右端点和最大,需要保留左 \(d+1\) 个数的最大值,这里可以用前缀最大值来维护。
用 \(pre_i\) 表示前 \(i\) 个数的最大值,那么递推式子就是 \(pre_i=max(pre_{i-1},a_i)\)
记得处理下 \(k\) 过大的情况即可,时间复杂度 \(O(n)\)
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 5e5 + 10;
int a[N], pre[N];
void solve() {
int n, k;
cin >> n >> k;
for (int i = 1; i <= n; i ++ ) {
cin >> a[i];
pre[i] = max(pre[i - 1], a[i]);
}
if (n == 1) {
cout << a[1] << endl;
return ;
}
int j = k + 1;
int ans = 0;
for (int i = n; i >= 2 && j >= 1; i -- ) {
ans = max(ans, a[i] + pre[min(j, i - 1)]);
j --;
}
cout << ans << endl;
}
int main() {
int T = 1;
cin >> T;
while (T -- ) solve();
return 0;
}
E 小苯的最短路
首先我们需要证明,在如题所示的完全图中,从 \(1\) 到任意一点 \(i\) 的最短路就是直接从 \(1\) 走到 \(i\) ,不绕路。接下来我们证明一下。
考虑反证法,假设存在一个点 \(i\) ,使得 \(1\) 到 \(i\) 的最短路是需要绕路的,我们设绕的这个点是 \(j\) 。那么一定有 \(i \oplus 1 \gt 1 \oplus j + j \oplus i\) ,由于 \(a \oplus b \le a + b\) ,所以
这与刚刚的式子矛盾,所以最短路就是直达,不用绕路。
知道了这个结论后,我们令 \(dis_i\) 为 \(1\) 到 \(i\) 的最短路,那么当输入为 \(n\) 的时候,我们只需要求 \(\bigoplus_{i=1}^n dis_i\) 即可,打表可以发现规律。
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
void solve() {
int n; cin >> n;
if (n % 4 == 0) {
cout << n << endl;
} else if (n % 4 == 1) {
cout << 0 << endl;
} else if (n % 4 == 2) {
cout << (n ^ 1) << endl;
} else {
cout << 1 << endl;
}
}
int main() {
int T = 1;
cin >> T;
while (T -- ) solve();
return 0;
}
F 小苯的优雅好序列
题目需要让 \(a\) 数组中的每个连续子数组都是优雅的,容易看出其实我们只需要让所有长度为 \(2\) 的连续子数组优雅就可以了。
那么接下来思考如何根据给定的 \(a_i\) 和 \(a_j\) 求出 \(x\) 。
我们不妨设 \(a_i \le a_j\) ,题目需要求出满足要求的 \((a_i + x) | (a_j + x)\) ,即存在一个正整数 \(k\) 满足
即
也就是 \((a_i + x) | (a_j - a_i)\)
所以我们只要枚举 \(a_j - a_i\) 的因子即可。最后的 \(x\) 其实就是用所有相邻两数之差 \(a_i - a_{i-1}\) 的因子求出的 \(x\) 的交集,所以我们先随便求一组差的解集,然后枚举所有的 \(x\) ,验证即可。
由于一个数 \(h\) 的因子数大概是 \(\log\) 级别的,所以最后的时间复杂度为 \(O(n \log a_i)\)
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 2e5 + 10;
const int INF = 0x3f3f3f3f;
int a[N], n;
bool check(int x) {
for (int i = 2; i <= n; i ++ ) {
int val = abs(a[i] - a[i - 1]);
if (val % (x + min(a[i], a[i - 1])) != 0) return false;
}
return true;
}
void solve() {
int k;
cin >> n >> k;
for (int i = 1; i <= n; i ++ ) cin >> a[i];
int pos = -1;
for (int i = 2; i <= n; i ++ ) {
if (a[i] == a[i - 1]) continue;
if (pos == -1 || abs(a[i] - a[i - 1]) < abs(a[pos] - a[pos - 1]))
pos = i;
}
if (pos == -1) {
cout << k << ' ' << 1ll * (1 + k) * k / 2 << endl;
return ;
}
int val = abs(a[pos] - a[pos - 1]);
vector<int> v;
for (int i = 1; i * i <= val; i ++ ) {
if (val % i) continue;
int v1 = i - min(a[pos], a[pos - 1]);
int v2 = val / i - min(a[pos], a[pos - 1]);
if (v1 > 0 && v1 <= k) v.push_back(v1);
if (v2 > 0 && v2 <= k) v.push_back(v2);
}
sort(v.begin(), v.end());
v.erase(unique(v.begin(), v.end()), v.end());
int cnt = 0;
LL sum = 0;
for (int x : v) {
// cout << x << ' ';
if (check(x)) {
cnt ++;
sum += x;
}
}
cout << cnt << ' ' << sum << endl;
}
int main() {
#ifndef ONLINE_JUDGE
freopen("1.in", "r", stdin);
freopen("1.out", "w", stdout);
#endif
int T; cin >> T;
while (T -- ) solve();
return 0;
}
G 小苯的树上操作
容易看出是换根dp,我们可以这样定义:
- \(dp1_{u, 2}\) 表示以 \(u\) 为根的子树,\(u\) 不去掉的最大点权和
- \(dp1_{u,0}\) 和 \(dp1_{u,1}\) 分别表示在以 \(u\) 为根的子树中,只保留一颗子树的最大权值和次大权值(不包括 \(u\) 的权值)
- \(dp2_{u,2}\) 表示以 \(u\) 为根,\(u\) 不去掉的最大点权和
- \(dp2_{u, 0}\) 和 \(dp2_{u,1}\) 分别表示在以 \(u\) 为根时,只保留一条相连的边的最大权值和次大权值(不包括 \(u\) 的权值)
接下来考虑转移
我们设当前树的根是 \(1\) ,遍历到 \(u\) 时,设 \(u\) 有 \(m\) 个孩子,其中 \(u\) 的第 \(i\) 个孩子为 \(v_i\)
在求 \(dp1_{u,2}\) 时,考虑从某个孩子 \(v_i\) 转移过来,可以考虑保留 \(v_i\) 节点,从 \(dp1_{v_i,2}\) 转移过来;也可以不保留 \(v_i\) 节点,直接去掉整个子树,也就是 \(0\) ;还可以从 \(v_i\) 的最大子树转移过来(对应题目中的第二种操作),也就是 \(dp1_{v_i,0}\) 。以上这些取 \(\max\) 即可。
在求 \(dp1_{u, 0}\) 和 \(dp1_{u, 1}\) 的时候,也和刚刚一样,用 \(max(dp1_{v_i,2}, dp1_{v_i, 0}, 0)\) 来更新即可。
在求 \(dp2_{u, 2}\) 的时候,设 \(u\) 的父节点是 \(w\) , 首先默认 \(dp2_{u,2} = dp1_{u,2}\) ,然后考虑加上 \(w\) 相关的值。 如果带上 \(w\) 本身,那么值是 \(dp2_{w, 2}\) ,但是这其中可能会有 \(u\) 相关的值,也就是 \(\max(dp1_{u, 2}, dp1_{u, 0}, 0)\) ,需要减去;如果不带上 \(w\) ,那么值就是 \(dp2_{w, 0}\) 。
但是这个值有可能就是 \(u\) 的子树的权值和,如果发现是的话,就要用 \(dp2_{w, 1}\) 来更新,以上取一个 \(\max\) 后加上即可。
在求 \(dp2_{u, 0}\) 和 \(dp2_{u, 1}\) 的时候,也和刚刚一样,用上面算出来的值更新即可。
时间复杂度 \(O(n)\)
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 2e5 + 10;
int a[N];
LL dp1[N][3], dp2[N][3];
vector<int> g[N];
void init(int n) {
for (int i = 1; i <= n; i ++ ) {
g[i].clear();
for (int j = 0; j < 3; j ++ )
dp1[i][j] = dp2[i][j] = 0;
}
}
void dfs1(int u, int last) {
dp1[u][2] = a[u];
for (int v : g[u]) {
if (v == last) continue;
dfs1(v, u);
LL val = max(dp1[v][2], dp1[v][0]);
if (val > 0) dp1[u][2] += val;
if (val > dp1[u][0]) {
dp1[u][1] = dp1[u][0];
dp1[u][0] = val;
} else if (dp1[v][2] > dp1[u][1]) {
dp1[u][1] = val;
}
}
}
void dfs2(int u, int last) {
for (int v : g[u]) {
if (v == last) continue;
dp2[v][0] = dp1[v][0];
dp2[v][1] = dp1[v][1];
dp2[v][2] = dp1[v][2];
LL val1 = dp2[u][2];
LL tmp = max(dp1[v][0], dp1[v][2]);
if (tmp > 0) val1 -= tmp;
LL val2 = dp2[u][0];
if (dp2[u][0] == tmp) val2 = dp2[u][1];
LL val = max(val1, val2);
dp2[v][2] += val;
if (val > dp2[v][0]) {
dp2[v][1] = dp2[v][0];
dp2[v][0] = val;
} else if (val > dp2[v][1]) {
dp2[v][1] = val;
}
dfs2(v, u);
}
}
void solve() {
int n; cin >> n;
init(n);
for (int i = 1; i <= n; i ++ ) cin >> a[i];
for (int i = 1; i < n; i ++ ) {
int u, v;
cin >> u >> v;
g[u].push_back(v);
g[v].push_back(u);
}
dfs1(1, 0);
dp2[1][0] = dp1[1][0];
dp2[1][1] = dp1[1][1];
dp2[1][2] = dp1[1][2];
dfs2(1, 0);
LL ans = 0;
for (int i = 1; i <= n; i ++ ) ans = max(ans, dp2[i][2]);
cout << ans << endl;
}
int main() {
#ifndef ONLINE_JUDGE
freopen("1.in", "r", stdin);
freopen("1.out", "w", stdout);
#endif
int T; cin >> T;
while (T -- ) solve();
return 0;
}