10.05模拟赛总结
比赛传送门
总结
\(100+60+0+0=160\),挂了 \(0+40+0+0=40\),Rank 16,寄寄寄寄寄。
T1 优秀 \(\texttt{/}\) \(\texttt{Good}\)
题意
求 \(l\) 和 \(r\) 之间的 \(2\) 的整数次幂。
分析
解法 1
由于 \(l\) 和 \(r\) 非常小,所以可以直接模拟,没啥好说的。
时间复杂度 \(O(r)\)。
解法 2
充分发扬人类智慧,发现肯定只有 \(2^{\left \lceil \log_2 l \right \rceil}\) 到 \(2^{\lfloor \log_2 r \rfloor}\) 这些数,循环输出就行了。
时间复杂度 \(O(\log_2 r)\)。
寄因
没寄。
\(\color{red}\text{不要忘记输出 cnt!!!}\)
\(\color{red}\text{不然……}\)
\(\color{red}{\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \,\!↑}\)
\(\color{red}{\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \!}没有输出\ cnt\ 的皮卡丘\)
代码
自己写吧,这么简单。
解法 1
#include <bits/stdc++.h>
using namespace std;
int l, r;
vector<int> a;
int main() {
cin >> l >> r;
int logl = log2(l);
for (int i = 1 << logl; i <= r; i <<= 1) {
if (i >= l) {
a.push_back(i);
}
}
cout << a.size() << '\n';
for (int i = 0; i < a.size(); i++) {
cout << a[i] << ' ';
}
return 0;
}
解法 2
#include <bits/stdc++.h>
using namespace std;
int l, r;
vector<int> a;
int main() {
cin >> l >> r;
int logl = ceil(log2(l)), logr = floor(log2(r));
for (int i = 1 << logl; i <= 1 << logr; i <<= 1) {
a.push_back(i);
}
cout << a.size() << '\n';
for (int i = 0; i < a.size(); i++) {
cout << a[i] << ' ';
}
return 0;
}
T2 词典 \(\texttt{/}\) \(\texttt{dict}\)
题意
超级强化版
正常题意
给定 \(n\) 个字符串 \(s\),\(q\) 次询问,每次询问给定一个字符串 \(t\),问能否进行以下操作最多一次将 \(t\) 变成任意的 \(s\),输出那个 \(s\)(保证唯一);否则输出 No:
-
在 \(t\) 中删除一个字母;
-
在 \(t\) 中插入一个字母;
-
在 \(t\) 中修改一个字母。
分析
数据范围很小,所以 unordered_map / map 模拟一下就行了,当然你想用 trie 也没人拦着你。
时间复杂度 \(O(\sum |t|)\),\(|t|\) 为字符串 \(t\) 的长度。
寄因
忘记判在最后加上字符的情况。
代码
#include <bits/stdc++.h>
using namespace std;
int n, q;
unordered_map<string, bool> dict;
int main() {
cin >> n >> q;
for (int i = 1; i <= n; i++) {
string s;
cin >> s;
dict[s] = 1;
}
for (int i = 1; i <= q; i++) {
string s;
cin >> s;
bool f = 1;
int l = s.size();
// -----------------------相等--------------------
if (dict[s]) {
cout << s << '\n';
continue;
}
// -------------------删去一个字符-----------------
for (int i = 0; i < l; i++) {
string s_ = s.substr(0, i) + s.substr(i + 1);
if (dict[s_]) {
cout << s_ << '\n';
f = 0;
break;
}
}
// -------------------加上一个字符-----------------
for (char i = 'a'; i <= 'z'; i++) {
for (int j = 0; j <= l; j++) {
string s_ = s.substr(0, j) + i + s.substr(j);
if (dict[s_]) {
cout << s_ << '\n';
f = 0;
break;
}
}
}
// -------------------修改一个字符-----------------
for (char i = 'a'; i <= 'z'; i++) {
for (int j = 0; j < l; j++) {
string s_ = s;
s_[j] = i;
if (dict[s_]) {
cout << s_ << '\n';
f = 0;
break;
}
}
}
if (f) {
cout << "No\n";
}
}
return 0;
}
T3 积木 \(\texttt{/}\) \(\texttt{block}\)
题意
现在搭了 \(n\) 列积木,第 \(i\) 列已经搭好的积木高 \(h_i\) 个,在 \(a_{i, j}\) 加上一个积木需要满足 \(a_{i - 1, j}, a_{i, j - 1}, a_{i + 1, j - 1}\) 都已经搭好了,求加上 \(m\) 块积木最大的高度,即 \(\max {h'}_i\)。
\(\texttt{p.s:}\ 1 \le n \le 10^5, 1 \le m \le 10^9, 1 \le h_i \le 10^5\)。
分析
看了这个数据范围,一眼顶针,望月身亡的,二分 \(m\),然后 \(O(n)\) 的 \(\operatorname{check}\),没了。
还有呢!对于 \(O(n)\) 的 \(\operatorname{check}\),实现可以运用单调性,\(L_i\) 表示以 \(i\) 为最高点向两边搭积木的最左边的位置,\(R_i\) 表示以 \(i\) 为最高点向两边搭积木的最左边的位置,\(O(n)\) 求一下 \(L_i,R_i\) 就可以了。
时间复杂度 \(O(n \log m)\)。
寄因
没写出来。
代码
#include <cstdio>
#include <vector>
using namespace std;
const long long inf = (long long)2e18;
const int N = 1234567;
int h[N];
vector<int> ev[N];
long long ans[N];
long long sum[N];
int main() {
int n;
long long m;
scanf("%d %lld", &n, &m);
int l = 0;
for (int i = 0; i < n; i++) {
scanf("%d", h + i);
l = max(l, h[i]);
}
int r = (int)l + m;
while (l < r) {
int mid = (l + r + 1) >> 1;
for (int i = 0; i < n; i++) {
ans[i] = 0;
}
for (int rot = 0; rot < 2; rot++) {
sum[0] = h[0];
for (int i = 1; i < n; i++) {
sum[i] = sum[i - 1] + h[i];
}
for (int i = 0; i < n; i++) {
ev[i].clear();
}
for (int i = 0; i < n; i++) {
int j = i + mid - h[i];
if (j < n) {
ev[j].push_back(i);
}
}
int mx = -1;
for (int i = 0; i < n; i++) {
for (int j = 0; j < (int)ev[i].size(); j++) {
mx = max(mx, ev[i][j]);
}
if (mx >= 0) {
int from = mid - 1;
int to = mid - (i - mx) + 1;
ans[i] += (to + from) * 1LL * (from - to + 1) / 2;
ans[i] -= (sum[i - 1] - sum[mx]);
} else {
ans[i] += inf;
}
}
for (int i = 0; i < n - i - 1; i++) {
swap(h[i], h[n - i - 1]);
swap(ans[i], ans[n - i - 1]);
}
}
bool found = 0;
for (int i = 0; i < n; i++) {
ans[i] += mid - h[i];
if (ans[i] <= m) {
found = 1;
break;
}
}
if (found) {
l = mid;
} else {
r = mid - 1;
}
}
printf("%d\n", l);
return 0;
}
T4 寻宝 \(\texttt{/}\) \(\texttt{trea}\)
题意
给定一棵树,有点权和边权,求从第 \(i\) 个点出发可以得到的最大的点权(每个点只计算一次) \(-\) 边权的值。
分析
设 \(f_1(x)\) 为 \(x\) 只往下走回到原点的答案,\(f_2(x)\) 为 \(x\) 只往下走不回到原点的答案。设 \(g_1(x)\) 为 \(x\) 只往上走回到原点的答案,\(g_2(x)\) 为 \(x\) 只往上走不回到原点的答案。第一遍树形 DP 从下往上求出 \(f_1\) 和 \(f_2\)。第二遍树形 DP 从上往下求出 \(g_1\) 和 \(g_2\)。然后就可以求出这个点的答案了。
寄因
没写出来。
代码
#include <algorithm>
#include <bitset>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
using namespace std;
typedef long long ll;
const int N = 610000;
int fi[N], ne[N], len[N], to[N];
ll dp0[N], dp1[N];
ll ans[N], wushi0[N], wushi1[N], val[N];
int cnt, n;
void add(int u, int v, int l) {
cnt++;
to[cnt] = v;
len[cnt] = l;
ne[cnt] = fi[u];
fi[u] = cnt;
}
void dfs(int u, int fa) {
ll mx = 0;
for (int j = fi[u]; j; j = ne[j]) {
if (to[j] == fa) {
continue;
}
dfs(to[j], u);
if (dp0[to[j]] - (len[j] << 1) > 0) {
dp0[u] += dp0[to[j]] - (len[j] << 1);
mx = max(mx, len[j] + dp1[to[j]] - dp0[to[j]]);
} else
mx = max(mx, dp1[to[j]] - len[j]);
}
dp0[u] += val[u];
dp1[u] = dp0[u] + mx;
}
void dfs2(int u, int fa, int le) {
ans[u] = max(dp0[u] + max(wushi1[u] - le, (ll)0), dp1[u] + max(wushi0[u] - (le << 1), (ll)0));
ll mx1 = 0, mx2 = 0;
for (int j = fi[u]; j; j = ne[j]) {
if (to[j] == fa) {
continue;
}
ll now;
if (dp0[to[j]] - (len[j] << 1) > 0) {
now = len[j] + dp1[to[j]] - dp0[to[j]];
} else {
now = dp1[to[j]] - len[j];
}
if (now > mx1) {
mx2 = mx1;
mx1 = now;
} else if (now > mx2) {
mx2 = now;
}
}
ll now;
if (wushi0[u] - 2 * le > 0) {
now = le + wushi1[u] - wushi0[u];
} else {
now = wushi1[u] - le;
}
if (now > mx1) {
mx2 = mx1;
mx1 = now;
} else if (now > mx2) {
mx2 = now;
}
for (int j = fi[u]; j; j = ne[j]) {
if (to[j] == fa) {
continue;
}
wushi0[to[j]] = dp0[u] - max((ll)0, dp0[to[j]] - (len[j] << 1)) + max((ll)0, wushi0[u] - (le << 1));
ll now;
if (dp0[to[j]] - (len[j] << 1) > 0) {
now = len[j] + dp1[to[j]] - dp0[to[j]];
} else {
now = dp1[to[j]] - len[j];
}
if (now == mx1) {
wushi1[to[j]] = wushi0[to[j]] + mx2;
} else {
wushi1[to[j]] = wushi0[to[j]] + mx1;
}
}
for (int j = fi[u]; j; j = ne[j]) {
if (to[j] != fa) {
dfs2(to[j], u, len[j]);
}
}
}
int main() {
cin >> n;
for (int i = 1; i < n; i++) {
int u, v, w;
cin >> u >> v >> w;
add(u, v, w);
add(v, u, w);
}
for (int i = 1; i <= n; i++) {
cin >> val[i];
}
dfs(1, 0);
for (int j = fi[1]; j; j = ne[j]) {
if (dp0[to[j]] - 2 * len[j] > 0) {
wushi0[to[j]] = dp0[1] - (dp0[to[j]] - 2 * len[j]);
} else {
wushi0[to[j]] = dp0[1];
}
}
ll mx1 = 0, mx2 = 0;
for (int j = fi[1]; j; j = ne[j]) {
ll now;
if (dp0[to[j]] - (len[j] << 1) > 0) {
now = len[j] + dp1[to[j]] - dp0[to[j]];
} else {
now = dp1[to[j]] - len[j];
}
if (now > mx1) {
mx2 = mx1;
mx1 = now;
} else if (now > mx2) {
mx2 = now;
}
}
for (int j = fi[1]; j; j = ne[j]) {
ll now;
if (dp0[to[j]] - 2 * len[j] > 0) {
now = len[j] + dp1[to[j]] - dp0[to[j]];
} else {
now = dp1[to[j]] - len[j];
}
if (now == mx1) {
wushi1[to[j]] = wushi0[to[j]] + mx2;
} else {
wushi1[to[j]] = wushi0[to[j]] + mx1;
}
}
ans[1] = dp1[1];
for (int j = fi[1]; j; j = ne[j]) {
dfs2(to[j], 1, len[j]);
}
for (int i = 1; i <= n; i++) {
cout << ans[i] << '\n';
}
return 0;
}


浙公网安备 33010602011771号