牛客小白月赛56题解
大概6月份想尝试出一出小白月赛。
想了几个题给兰子看,兰子毙了一个最难的,加了个签到。
现在的F原来的条件,意识到是假做法,然后自己毙了,就成了现在的模样。
感觉我的std都好麻烦,大家的代码都好简短。
F题我是按照分层图去想的,内测时大部分人都是一个简短的建图搞过去...
赛时有人指出B题,没说输出怎么分割(空格?逗号?....)谢罪。
A、阿宁的柠檬
最小值,每个柠檬取最小酸度和最小甜度。
最大值,每个柠檬取最大酸度和最大甜度。
#include <bits/stdc++.h> using namespace std; using LL = long long; int a, b, n; int main() { cin >> a >> b >> n; cout << n << ' ' << (LL)(a + b) * n << endl; return 0; }
B、阿宁与猫咪
时,数组全是
,
和
都是
。如果选择其它的构造方式,
至少加
,
最少等于
。所以数组全是
为最优的策略。
时,
是
,
是
。
#include <bits/stdc++.h> using namespace std; int m; int main() { cin >> m; cout << m << endl; for (int i = 1; i <= m; ++i) { cout << 1 << " "; } return 0; }
C、阿宁吃粽子
贪心,美味值越大的粽子,应该放到 越大的位置。
先将余数是 0,1,2,3 ... 9 的位置,分别有多少个先算出来,再把粽子分类。在把每个位置的粽子对应出一个顺序,输出。
#include <bits/stdc++.h> using namespace std; const int N = 3 + 2e5; int n, a[N]; vector<int> b[10]; int c[10]; int main() { cin >> n; for (int i = 1; i <= n; ++i) { cin >> a[i]; } sort(a + 1, a + n + 1); int p; for (int i = 1; i <= 10; ++i) { // 算每个余数有多少个坑 p = i % 10; c[p] = n / 10; if (i <= n % 10) ++c[p]; } p = 0; for (int i = 1; i <= n; ++i) { // 按美味值从小到大放每个粽子 if (b[p].size() == c[p]) p = (p + 1) % 10; b[p].push_back(a[i]); } p = 0; for (int i = 0; i < b[1].size(); ++i) { for (int j = 1; j <= 10; ++j) { if (i < b[j % 10].size()) a[++p] = b[j % 10][i]; } } for (int i = 1; i <= n; ++i) { cout << a[i] << ' '; } return 0; }
D、阿宁的质数
先使用 的方法(线性筛也行)筛出质数,打表。后面的判断质数都通过查表。
从左往右预处理答案,假设当前是 。维护一个变量
,表示区间
的答案(最小未出现的质数)。还要维护一个
,存区间
的质数。
当 是质数,就把
放到
里面。然后判断
满足:是不是出现在
里面或者是不是质数。如果满足则
加
。
是只增不减的,单次的加
复杂度是
。
#include <bits/stdc++.h> using namespace std; const int N = 3 + 2e5; int n, q, a[N]; bool p[N * 20]; int pre[N]; set<int> st; // 第2e5个质数 2750159 // 至少要第2e5+1个质数 int main() { cin >> n >> q; for (int i = 1; i <= n; ++i) { cin >> a[i]; } memset(p, true, sizeof(p)); p[1] = false; for (int i = 2; i < N * 20; ++i) { if (p[i]) { for (int j = i + i; j < N * 20; j += i) { p[j] = false; } } } int now = 1; for (int i = 1; i <= n; ++i) { if (a[i] < N * 20 && p[a[i]]) st.insert(a[i]); while (!p[now] || st.count(now)) ++now; pre[i] = now; } while (q--) { int x; cin >> x; cout << pre[x] << endl; } return 0; }
E、阿宁睡大觉
将字符串分割,连续大写 'Z' 放一段,小写 'z' 放一段。比如 "zzzzZZzZZZZzzzZZZ"分割为["zzzz","ZZ","z","ZZZZ",'zzz','ZZZ']。
删除一段小写,使得两段大写合并,总权值才会加 。为了节省操作次数,很明显我们应该优先将短的小写段删除。比如前面的例子,我们应该先删除 'z',再删除 'zzz'。
#include <bits/stdc++.h> using namespace std; const int N = 3 + 2e5; int n, k; char s[N]; vector<int> b; int main() { cin >> n >> k; cin >> s + 1; int ans = 0; while (n && s[n] == 'z') { --n; } if (n == 0) { cout << 0 << endl; return 0; } for (int i = 1; i <= n; ++i) { if (s[i] == 'z') continue; int j = i; while (j + 1 <= n && s[j + 1] == 'Z') ++j; // i 到 j 是一段大写的 'Z' ans += 4 * (j - i); // 这一段产生的贡献 j = j + 1; i = j; if (i > n) break; while (j + 1 <= n && s[j + 1] == 'z') ++j; // 如果删除这一段小写 'z',两端大写合并产生的贡献是 4 // 因为这样写,所以前面有先把后缀的小写 'z' 删掉 b.push_back(j - i + 1); i = j; } sort(b.begin(), b.end()); for (auto &i : b) { if (i <= k) { // 贪心先将长度小的段删掉 ans += 4; k -= i; } } cout << ans << endl; return 0; }
F、阿宁去游玩
最短路,定义一下状态,跑dijkstra。
状态定义: 从
号从到
号城市的最短路,并且,
次膜法对
号城市生效,在
号城市使用了
次膜法。
因为使用两次膜法相当没使用,因此 、
都
。
转移:
在
号城市使用膜法:
从
号城市前往
号城市:
对号城市生效的膜法也对
号城市生效,还有在
号城市使用的膜法。
根据定义的状态就可以知道 和
之间是不是同样的属性,从而知道边权。
(在 使用膜法,不会对来
城市之前的城市产生影响,权值变大了,回不去了...或者说最短路每个点只会走一次...)
#include <bits/stdc++.h> using namespace std; using LL = long long; const int N = 3 + 2e5; struct Node { int x, i, j; LL d; bool operator<(const Node &b) const { return d > b.d; } }; int n, m, x, y, z; int a[N]; vector<int> edge[N]; LL d[N][2][2]; bool vis[N][2][2]; priority_queue<Node> q; int main() { cin >> n >> m; cin >> x >> y >> z; for (int i = 1; i <= n; ++i) { cin >> a[i]; } while (m--) { int u, v; cin >> u >> v; edge[u].push_back(v); edge[v].push_back(u); } memset(d, 0x3f, sizeof(d)); d[1][0][0] = 0; q.push({1, 0, 0, 0LL}); while (q.size()) { auto node = q.top(); int &u = node.x, &i = node.i, &j = node.j; q.pop(); if (u == n) { cout << d[u][i][j] << endl; return 0; } if (vis[u][i][j]) continue; vis[u][i][j] = true; if (j == 0) { if (d[u][i][1] > d[u][i][0] + z) { // 使用一次倒转膜法 d[u][i][1] = d[u][i][0] + z; q.push({u, i, 1, d[u][i][1]}); } } for (int &v : edge[u]) { int w = 0; if ((i + a[u]) % 2 == (i + j + a[v]) % 2) { // 和下一个点状态是否相同 w = x; } else { w = y; } if (d[v][(i + j) % 2][0] > d[u][i][j] + w) { d[v][(i + j) % 2][0] = d[u][i][j] + w; q.push({v, (i + j) % 2, 0, d[v][(i + j) % 2][0]}); } } } assert(false); return 0; }