回头看,轻舟已过万重山.|

Ke_scholar

园龄:2年2个月粉丝:30关注:10

2024-12-10 17:10阅读: 9评论: 0推荐: 0

牛客周赛 Round 70

小苯晨跑

思路

判全部是否相等即可。

代码

#include <bits/stdc++.h>
using namespace std;
using i64 = long long;
void solve() {
int a[5]{};
for(int i = 0;i < 4;i ++){
cin >> a[i];
}
for(int i = 1;i < 4;i ++){
if(a[i] != a[i - 1]){
cout << "YES\n";
return ;
}
}
cout << "NO\n";
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int t;
cin >> t;
while (t--) {
solve();
}
return 0;
}

小苯过马路

思路

分类讨论。

  • 当前是绿灯的时候:
    • 剩下的时间够通过马路:则花费 \(t\) 时间。
    • 剩下的时间不够过马路,需等到下一次绿灯,中间等一次红灯:花费 \(t + k + x\) 时间。
  • 当前是红灯:直接等到绿灯即可:花费 \(k + t\) 时间。

代码

#include <bits/stdc++.h>
using namespace std;
using i64 = long long;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int x,y,k,t;
char c;
cin >> x >> y >> k >> t >> c;
if(c == 'G'){
if(k >= t){
cout << t << "\n";
}else{
cout << t + k + x << "\n";
}
}else{
cout << k + t << "\n";
}
return 0;
}

小苯的字符串染色

思路

因为区间长度为 \(1\) 的染色为白色,且构造次数小于等于 \(n\) 次,那么直接对每个 \(i\) 染色即可。

代码

#include <bits/stdc++.h>
using namespace std;
using i64 = long long;
void solve() {
int n;
cin >> n;
string s;
cin >> s;
cout << n << "\n";
for(int i = 1;i <= n;i ++){
cout << i << " " << i << "\n";
}
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int t;
cin >> t;
while (t--) {
solve();
}
return 0;
}

小苯的能量项链

思路

因为每次操作都会去掉一个数,所以我们最后能保留下来的数最少是 \(\max(2,n-k)\) 个,在这个区间两边的都有可能被保留下来,那么我们只要维护这个区间两边的最大值即可,即预处理一遍前缀后缀最大值。

代码

#include <bits/stdc++.h>
using namespace std;
using i64 = long long;
void solve() {
int n, k;
cin >> n >> k;
vector<int> a(n + 1);
for (int i = 1; i <= n; i ++) {
cin >> a[i];
}
if (n == 1) {
cout << a[1] << "\n";
return ;
}
vector<int> pre(n + 1), suf(n + 1);
pre[1] = a[1], suf[n] = a[n];
for (int i = 2; i <= n; i ++) {
pre[i] = max(a[i], pre[i - 1]);
}
for (int i = n - 1; i >= 1; i --) {
suf[i] = max(suf[i + 1], a[i]);
}
int ans = 0;
int len = max(2, n - k);
for (int i = 1; i + len - 1 <= n; i ++) {
ans = max(ans, pre[i] + suf[i + len - 1]);
}
cout << ans << "\n";
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int t;
cin >> t;
while (t--) {
solve();
}
return 0;
}

小苯的最短路

思路

结论题,然而我是通过打表发现的。

一共用了两个结论。

结论 \(1\)\(1\oplus i \le 1\oplus j + j \oplus i.\)

由已知结论 \(a \oplus b \le a + b,0\oplus x=x,x\oplus x=0\) ,简单证明上述结论:

\[\because 1 \oplus i = 1\oplus j\oplus j\oplus i\\\\ 设 a = 1\oplus j,b=j\oplus i\\\\ 则根据 a\oplus b \le a+b可得:\\\\ 1\oplus i = a\oplus b\le a+b=1\oplus j + j\oplus i\\\\ 即证\ 1\oplus i \le 1\oplus j + j\oplus i \]

所以由上述结论可知,从 \(1\) 出发的最短路一定是 \(1\) 直达最短,不用中转。

所以最后的最短路的值的异或和即答案为 \(\bigoplus\limits_{i=1}^ni\),这个也同样有个结论,证明[1]网上也能搜到,在此不过多赘述。

结论 \(2\) :设 \(x=\bigoplus\limits_{i=1}^ni\),则有:

  • \(x \equiv 0\pmod 4,x=n.\)
  • \(x \equiv 1\pmod 4,x=1.\)
  • \(x \equiv 2\pmod 4,x=n+1.\)
  • \(x \equiv 3\pmod 4,x=0.\)

代码

#include <bits/stdc++.h>
using namespace std;
using i64 = long long;
void solve() {
int n;
cin >> n;
if (n & 1) {
n--;
cout << (n % 4 !=0) << "\n";
} else {
cout << n + (n % 4 != 0) << "\n";
}
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int t;
cin >> t;
while (t--) {
solve();
}
return 0;
}

小苯的优雅好序列

思路

要使得所有的连续子数组都满足优雅的,那么可以得出一个结论:

  • 对于相邻的 \(a_i\)\(a_{i+1}\) 一定满足 \(a_i \mid a_{i+1}\) 或者 \(a_{i+1} \mid a_i\)

简单证明:子数组长度为 \(1\) 时,显然满足条件;子数组长度为 \(2\) 时,要使得其优雅,也就是这两个数之间要满足整除关系,由此可得该结论。

现在问加上一个 \(x\) 后是否满足该数组是一个好数组,那么同样也可以得到一个结论:

  • 相邻两数之间的差值都满足是较小数加上 \(x\) 的倍数。

设相邻两数为 \(a,b\),有 \(a\le b\),简单证明:

\[加上 x 后是优雅的,应该满足\\\\ a + x \mid b + x\\\\ 即\ b + x = k(a+x) = (k-1)(a+x)+a+x\\\\ b - a = (k-1)(a+x)\\\\ 即证:a+x \mid b-a \]

所以对于一个 \(x\),要判断数组都加上它后是否是一个好数组,那么只要 \(O(n)\) 检查一遍上述结论即可。

那么还有一个问题,检查的范围,总不能对 \(1\sim k\) 都检查一遍吧。
还是利用上面的结论,通过上述结论可以发现,所有的相邻数都应该满足这样一个结论,那么我们只要找任意一组相邻且不相等的两数,把它们的差值拿出验证其他相邻对是否满足这样一个关系即可。

复杂度 \(O(\sqrt{\max a_i}\times n)\)\(1\sim 1\times 10^9\) 以内最大的因子数为 \(1344\),实际上并不会跑满。

代码

#include <bits/stdc++.h>
using namespace std;
using i64 = long long;
void solve() {
int n, k;
cin >> n >> k;
array<int, 2> has{0,0};
vector<int> a(n + 1);
for (int i = 1; i <= n; i ++) {
cin >> a[i];
if (i > 1&& a[i] != a[i - 1]) {
has = {a[i - 1], a[i]};
}
}
int d = abs(has[0] - has[1]);
if (d == 0) {
cout << k << " " << 1LL * k * (k + 1) / 2 << "\n";
return ;
}
i64 cnt = 0, ans = 0;
auto check = [&](int x)->void{
x -= min(has[0], has[1]);
if (x <= 0) {
return ;
}
auto ok = [&](int pos, int idx)->bool{
if (idx < 1 || idx > n) return false;
int s = abs(a[pos] - a[idx]);
return s % (a[pos] + x) == 0 || s % (a[idx] + x) == 0;
};
for (int i = 1; i <= n; i ++) {
if (!ok(i, i + 1) && !ok(i, i - 1)) {
return ;
}
}
if (x > k) return;
cnt ++ , ans += x;
};
for (int i = 1; i <= d / i; i ++) {
if (d % i == 0) {
check(i);
if (i != d / i) {
check(d / i);
}
}
}
cout << cnt << " " << ans << "\n";
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int t;
cin >> t;
while (t--) {
solve();
}
return 0;
}

小苯的树上操作

思路

赛时没写出来,参考下官方题解[2]

\(dp_i\) 表示以 \(i\) 为根,删除若干结点后得到的最大子树和。

对于结点 \(u\) 来说,如果不删除结点 \(u\),那么 \(dp_u=\sum\limits_{v\in S}dp_v[dp_v\ge 0]\) (\(S\) 代表 \(u\) 的子树);如果删除除结点 \(i\) 以外的根结点 \(u\),那么由于要删除度数恰好为 \(2\) 的,所以最多只能保留一棵子树,即 \(dp_u=\max(0,\max\limits_{v\in S}dp_v)\)

特别的,把根结点删除后,其实是最多两棵子树的合并,所以维护答案 \(f_u\) 表示以 \(u\) 为根的答案,那么从 \(f_u\) 转移到 \(f_v\) 的过程,可以维护一个值 \(up\),表示父结点 \(u\) 删掉除去子结点 \(v\) 后剩下子树的最大子树和,和 \(dp_i\) 的计算方法类似,只不过放到了递归中去维护。

代码

#include <bits/stdc++.h>
using namespace std;
using i64 = long long;
void solve() {
int n;
cin >> n;
vector<int> a(n + 1);
for (int i = 1; i <= n; i ++) {
cin >> a[i];
}
vector g(n + 1, vector<int>());
for (int i = 1; i < n; i ++) {
int u, v;
cin >> u >> v;
g[u].emplace_back(v);
g[v].emplace_back(u);
}
vector<i64> dp(n + 1);
auto dfs = [&](auto && self, int u, int fa)->void{
dp[u] += a[u];
multiset<i64, greater<>> s;
for (auto &v : g[u]) {
if (v == fa)continue;
self(self, v, u);
dp[u] += dp[v] * (dp[v] > 0);
s.insert(dp[v]);
}
dp[u] = max({dp[u], 0LL, *s.begin()});
};
dfs(dfs, 1, 0);
vector<i64> f(n + 1);
auto dfs1 = [&](auto && self, int u, int fa, i64 up)->void{
i64 all = a[u] + up;
multiset<i64, greater<>> s;
s.insert(up);
for (auto &v : g[u]) {
if (v == fa)continue;
all += dp[v] * (dp[v] > 0);
s.insert(dp[v]);
}
f[u] = max(all, *s.begin() + (s.size() > 1 ? *next(s.begin()) : 0LL));
for (auto &v : g[u]) {
if (v == fa)continue;
s.erase(s.lower_bound(dp[v]));
self(self, v, u, max(all - dp[v], s.size() ? *s.begin() : 0LL));
s.insert(dp[v]);
}
};
dfs1(dfs1, 1, 0, 0);
cout << *max_element(f.begin() + 1, f.end()) << "\n";
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int t;
cin >> t;
while (t--) {
solve();
}
return 0;
}

  1. https://www.cnblogs.com/Mychael/p/8633365.html ↩︎

  2. https://ac.nowcoder.com/discuss/1442948?type=101∨der=0&pos=2&page=0&channel=-1&source_id=1 ↩︎

本文作者:Ke_scholar

本文链接:https://www.cnblogs.com/Kescholar/p/18590405

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   Ke_scholar  阅读(9)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起