牛客周赛 Round 106
写在前面
比赛地址:https://ac.nowcoder.com/acm/contest/116002
无奖竞猜为什么今天在做算法题(
A
显然对于 \(n\bmod 2 = 1\),总格子数量为奇数,用 \(1\times 2\) 的方格一定无解。
然后发现对于 \(n\bmod 2 = 0\) 类似样例地构造一定有解。
//
/*
By:Luckyblock
*/
#include <bits/stdc++.h>
#define LL long long
//=============================================================
//=============================================================
//=============================================================
int main() {
//freopen("1.txt", "r", stdin);
std::ios::sync_with_stdio(0), std::cin.tie(0);
int T = 1;
while (T --) {
int n; std::cin >> n;
std::cout << (n % 2 == 0 ? "YES" : "NO");
}
return 0;
}
B
\(k\le 20\),数据范围很小,直接模拟检查是否有解即可。
//
/*
By:Luckyblock
*/
#include <bits/stdc++.h>
#define LL long long
//=============================================================
//=============================================================
bool check(LL x_) {
LL x = x_;
std::vector<int> vec;
while (x) {
vec.push_back(x % 10);
x /= 10;
}
for (int i = 0, j = vec.size() - 1; i < j; ++ i, -- j) {
if (vec[i] != vec[j]) return false;
}
return true;
}
void add(LL &x_) {
LL x = x_, y = 0;
std::vector<int> vec;
while (x) {
vec.push_back(x % 10);
x /= 10;
}
for (auto it : vec) {
y = 10ll * y + it;
}
x_ = x_ + y;
}
//=============================================================
int main() {
//freopen("1.txt", "r", stdin);
std::ios::sync_with_stdio(0), std::cin.tie(0);
int T; std::cin >> T;
while (T --) {
LL n, k, ans = -1; std::cin >> n >> k;
for (int i = 0; i <= k; ++ i) {
if (check(n)) {
ans = i;
break;
} else if (i < k) {
add(n);
}
}
std::cout << n << " " << ans << "\n";
}
return 0;
}
C
保证 \(a_5\) 之后均合法,显然仅需考虑 \(a_1, a_2, a_3, a_4\) 的关系即可。
又仅需考虑个位数的乘积,则由小学数学可知,构造 \(a_1, a_2\) 时实际上仅需考虑它们的个位数的情况即可。又需要保证字典序最小,则对于范围 \([L_1, R_1], [L_2, R_2]\),其中有贡献的区间实际仅有:\([L_1, L_1 + 9], [L_2, L_2 + 9]\)。
则考虑枚举 \(a_1\in [L_1, L_1 + 9], a_2 \in [L_2, L_2 + 9]\) 然后大力检查即可。
//
/*
By:Luckyblock
*/
#include <bits/stdc++.h>
#define LL long long
//=============================================================
//=============================================================
bool check(int a1_, int a2_, int a3_, int a4_) {
a1_ %= 10, a2_ %= 10;
if (a1_ * a2_ % 10 == a3_) {
if (a4_ != -1) return a2_ * a3_ % 10 == a4_;
return true;
}
return false;
}
//=============================================================
int main() {
//freopen("1.txt", "r", stdin);
std::ios::sync_with_stdio(0), std::cin.tie(0);
int T; std::cin >> T;
while (T --) {
int n, L1, R1, L2, R2, ans1 = -1, ans2 = -1;
std::cin >> n >> L1 >> R1 >> L2 >> R2;
std::vector<int> a(n + 2, -1);
for (int i = 3; i <= n; ++ i) std::cin >> a[i];
for (int a1 = L1; a1 <= std::min(R1, L1 + 10); ++ a1) {
for (int a2 = L2; a2 <= std::min(R2, L2 + 10); ++ a2) {
if (check(a1, a2, a[3], a[4])) {
ans1 = a1, ans2 = a2;
break;
}
}
if (ans1 != -1) break;
}
std::cout << ans1 << " " << ans2 << "\n";
}
return 0;
}
D
将原序列变为回文序列,则仅需分别考虑,如何令对称的两个位置相等即可。
令 \(a_i\) 变为 \(a_i \oplus \left\lfloor \frac{a_i}{2} \right\rfloor\) 实际上是令 \(a_i\) 异或 \(a_i\) 右移一位。然后手玩下发现对于某个数操作不会太多次之后就有循环节了,于是可以大力预处理出所有操作结果以及步数。
具体为什么有循环节?显然上述操作方式下最高位的 1 永远不变,手玩下发现在大概 \(O(\log x)\) 级别次操作后,即可令 \(x\) 变为 01 交替的形式,再操作一次后即可变成全 1,在操作一次后就只剩最高位的 1,然后就有了循环节。到达循环节的步数以及循环节的长度都大概是 \(O(\log x)\) 级别。
然后对于所有对称的两个位置,枚举所有公共的操作结果并求最小步数之和即可。
//
/*
By:Luckyblock
*/
#include <bits/stdc++.h>
#define LL long long
const int kInf = 1e9 + 2077;
//=============================================================
//=============================================================
int solve(int x_, int y_) {
std::map<int, int> cntx, cnty;
cntx[x_] = cnty[y_] = 0;
for (int i = 1, pre = x_, now = -1; ; ++ i, pre = now) {
now = pre ^ (pre >> 1);
if (cntx.count(now)) break;
cntx[now] = i;
}
for (int i = 1, pre = y_, now = -1; ; ++ i, pre = now) {
now = pre ^ (pre >> 1);
if (cnty.count(now)) break;
cnty[now] = i;
}
int ret = kInf;
for (auto [key, val]: cntx) {
if (cnty.count(key)) ret = std::min(ret, val + cnty[key]);
}
return ret;
}
//=============================================================
int main() {
//freopen("1.txt", "r", stdin);
std::ios::sync_with_stdio(0), std::cin.tie(0);
int T; std::cin >> T;
while (T --) {
int n, ans = 0; std::cin >> n;
std::vector<int> a(n + 1);
for (int i = 1; i <= n; ++ i) std::cin >> a[i];
for (int i = 1, j = n; i < j; ++ i, -- j) {
int ret = solve(a[i], a[j]);
if (ret == kInf) {
ans = -1;
break;
}
ans += ret;
}
std::cout << ans << "\n";
}
return 0;
}
/*
a = bn bn-1 ... b3 b2 b1
a/2 = 0 bn ... b4 b3 b2
0001111000
0000111100
0001000100
0001000100
0000100010
0001100110
0001100110
0000110011
0001010101
0001010101
0000101010
0001111111
0001111111
0000111111
0001000000
0001000000
0000100000
0001100000
0001100000
0000110000
0001010000
0001010000
0000101000
0001111000
0001111000
0000111100
0001000100
101010
010101
111111
*/
E
显然当且仅当 \(\operatorname{sum} \le n\) 时无解。
对于 \(n\le 4 n\) 时,发现令 1 变为 4 代价是 \(\operatorname{sum}-3\),4 变为 8 代价是 \(\operatorname{sum}-4\),则显然此时仅使用 1 和 4 是最好的,于是仅需求最多能有多少 4。
对于 \(n\ge 4 n\) 时,先令所有位置为 4。考虑如何每次令一个数字中洞的数量 \(+1\),容易发现有用的数字仅有 4 和 8。且构造规律为:
- 若当前所有位置为 \(48\cdots 88\),尝试令所有位置变为 \(88\cdots 88\);
- 若当前所有位置为 \(88\cdots 88\),尝试令所有位置变为 \(888\cdots 88\);
发现改变的轮数不超过 20 次,于是预处理一下变化时 \(\operatorname{sum}\) 的变化量,然后直接大力模拟即可。
//
/*
By:Luckyblock
*/
#include <bits/stdc++.h>
#define LL long long
//=============================================================
std::map<LL, LL> next;
//=============================================================
void init() {
next[4] = 4;
for (LL i = 8, j = 40; i <= 1e10; i += 2 * j, j *= 10ll) {
next[i] = next[i + j] = j;
}
}
//=============================================================
int main() {
//freopen("1.txt", "r", stdin);
std::ios::sync_with_stdio(0), std::cin.tie(0);
init();
int T; std::cin >> T;
while (T --) {
int n; LL sum; std::cin >> n >> sum;
if (sum < n) {
std::cout << -1 << "\n";
continue;
} else if (sum <= 4 * n) {
sum -= n;
for (int i = 1; i <= n; ++ i) {
sum -= 3;
std::cout << (sum >= 0 ? 4 : 1) << " ";
}
std::cout << "\n";
continue;
}
sum -= 4 * n;
std::vector<LL> ans(n + 1, 4);
while (sum >= next[ans[n]]) {
for (int i = 1; i <= n; ++ i) {
LL c = next[ans[i]];
if (sum >= c) ans[i] += c, sum -= c;
}
}
for (int i = 1; i <= n; ++ i) std::cout << ans[i] << " ";
std::cout << "\n";
}
return 0;
}
/*
只使用 1 4 8
8 8
48 8
88 8
88 48
88 88
488 488
888 488
888 888
1 1 1 1 1 1 ……
1 4 4 4 4 4 ……
4 4 4 4 4 4 ……
4 4 4 8 8 8 ……
8 8 8 8 8 8 ……
8 8 8 80 88 88 88 …… (80 不超过 1 个)
80 88 88 88 88 ……
880 888 888 888 …… (880 不超过 1 个)
考虑每次加一个洞的代价
*/
F
考虑钦定每个位置 \(i\) 为一段合法子数组的 \(b_1/b_m\) 的较小一方,然后考虑该位置是左边界 \(b_1\) 还是右边界 \(b_m\)。
\(a_i\) 若为左边界 \(b_1\),则需要找到 \(i\) 右侧的所有位置 \(j\),满足 \(\forall i\le k <j, a_j > a_i > a_k\),即 \(a_j\) 是右侧第一个大于 \(a_i\) 的元素,且 \([i, j]\) 中不能有等于 \(a_i\) 的元素,这等价于:找到 \(i\) 右侧第一个大于等于 \(a_i\) 的元素 \(a_j\),然后判断 \(a_j\not= a_i\)。
单调栈实现即可,同理可以得到 \(a_i\) 为右边界 \(b_m\) 的贡献。
//
/*
By:Luckyblock
*/
#include <bits/stdc++.h>
#define LL long long
const int kN = 2e5 + 10;
//=============================================================
int n, top, a[kN], st[kN];
int L[kN], R[kN], cntL[kN], cntR[kN];
//=============================================================
//=============================================================
int main() {
//freopen("1.txt", "r", stdin);
std::ios::sync_with_stdio(0), std::cin.tie(0);
int T; std::cin >> T;
while (T --) {
std::cin >> n;
for (int i = 1; i <= n; ++ i) {
std::cin >> a[i];
L[i] = 0, R[i] = n + 1;
cntL[i] = cntR[i] = 0;
}
top = 0;
for (int i = 1; i <= n; ++ i) {
while (top && a[i] >= a[st[top]]) R[st[top]] = i, -- top, ++ cntR[i];
st[++ top] = i;
}
top = 0;
for (int i = n; i; -- i) {
while (top && a[i] >= a[st[top]]) L[st[top]] = i, -- top, ++ cntL[i];
st[++ top] = i;
}
LL ans = 0;
for (int i = 1; i <= n; ++ i) {
if (L[i] && a[L[i]] > a[i] && i - L[i] >= 2) ++ ans;
if (R[i] != n + 1 && a[R[i]] > a[i] && R[i] - i >= 2) ++ ans;
}
std::cout << ans << "\n";
}
return 0;
}
/*
1
6
5 1 1 1 4 4
5 1 3 4 5 6
*/
写在最后
这么套 b 题做了两个小时,汤碗。

浙公网安备 33010602011771号