Codeforces Round 820
写在前面
比赛地址:https://codeforces.com/contest/1729。
赛时过了 D,哈希不会写跑路了哈哈。
A
\(t\) 组数据,每组数据给定参数 \(a,b,c\)。
现有两台电梯,第一台电梯在 \(a\) 层,第二台电梯刚刚从 \(b\) 层离开,前往 \(c\) 层。电梯从 \(x\) 层到 \(y\) 层需要花费 \(|x-y|\) 的时间。现在一个人在 \(1\) 层,如果呼叫第一台电梯,电梯会立刻前往 \(1\) 层,如果呼叫第二台电梯,电梯会在到达 \(c\) 层后立刻返回 \(1\) 层。这个人想要等待最少时间,问应当呼叫哪台电梯。
\(1\le t\le 10^4\),\(1\le a,b,c\le 10^8\),\(b\not= c\)。
1S,256MB。
水。
//By:Luckyblock
/*
大学傻逼
*/
#include <stdio.h>
#include <ctype.h>
//=============================================================
//=============================================================
int read() {
int f = 1, w = 0; char ch = getchar();
for (; !isdigit(ch); ch = getchar()) if (ch == '-') f = -1;
for (; isdigit(ch); ch = getchar()) w = 10 * w + ch - '0';
return f * w;
}
//=============================================================
int main() {
int T = read();
while (T --) {
int a = read() - 1, b = read(), c = read();
int time2 = c > b ? c - b + c - 1 : b - 1;
if (a < time2) printf("1\n");
else if (a > time2) printf("2\n");
else printf("3\n");
}
return 0;
}
B
定义一种字符串的加密方式:
- \(s\) 是一个仅包含小写英文字母的字符串。
- 从左到右遍历 \(s\),对于 \(s\) 的每一个字母,记它在字母表中的顺序为 \(k\),按照如下方式进行变化:
- 如果该字符在字母表的顺序小于 10,则变为 \(k\) 对应的字符串。
- 否则,变为 \(k\) 对应的字符串后,在末尾添加字符 \(0\)。
\(q\) 组数据,每组数据给定一个整数 \(n\),给定一个长度为 \(n\) 的仅包含字符 \(0\sim 9\) 的、由某个 \(s\) 加密得到的字符串 \(t\)。求加密前的字符串 \(s\)。
\(1\le q\le 10^4\),\(1\le n\le 50\)。
1S,256MB。
模拟。
从左到右遍历 \(t\),若有 0 则解密 0 前面的两位,否则解密 1 位。
//By:Luckyblock
/*
大学傻逼
*/
#include <stdio.h>
#include <ctype.h>
//=============================================================
char ch, s[110];
//=============================================================
int read() {
int f = 1, w = 0; char ch = getchar();
for (; !isdigit(ch); ch = getchar()) if (ch == '-') f = -1;
for (; isdigit(ch); ch = getchar()) w = 10 * w + ch - '0';
return f * w;
}
//=============================================================
int main() {
int T = read();
while (T --) {
int n = read();
for (int i = 1; i <= 100; ++ i) s[i] = 'a';
scanf("%s", s + 1);
for (int i = 1; i <= n; ) {
if (s[i + 2] == '0' && s[i + 3] != '0') {
ch = 10 * (s[i] - '0') + s[i + 1] - '0' + 'a' - 1;
i += 3;
} else {
ch = s[i] - '0' + 'a' - 1;
i ++;
}
printf("%c", ch);
}
printf("\n");
}
return 0;
}
C
定义一个游戏跳砖块:
- 游戏的地图为长度为 \(n\) 的一行方格,每个方格里有一个小写字母,。
- 玩家的初始位置为第 1 个方格。
- 玩家每次操作可以移动到任意的方格上,代价为两方格字母在字母表中的距离。
- 到达方格 \(n\) 时游戏结束。
\(t\) 组数据,每组数据给定字符串 \(s\),表示游戏的地图。要求在花费最少代价结束游戏的前提下,使用最多的操作次数,求代价和操作方案。
\(1\le t\le 10^4\),\(2\le |s|\le 2\times 10^5\),\(\sum |s|\le 2\times 10^5\)。
1S,256MB。
模拟。
显然最小代价为 \(|s_n - s_1|\)。操作次数最多,只需把表格中 \(s_1\sim s_n\) 中所有字母按顺序跳一遍即可。
//By:Luckyblock
/*
大学傻逼
a b c d e f g h i j k l m n o p q r s t u v w x y z
*/
#include <cstdio>
#include <cctype>
#include <cstring>
#include <vector>
const int kN = 2e5 + 10;
//=============================================================
char s[kN];
std::vector <int> pos[26];
//=============================================================
int read() {
int f = 1, w = 0; char ch = getchar();
for (; !isdigit(ch); ch = getchar()) if (ch == '-') f = -1;
for (; isdigit(ch); ch = getchar()) w = 10 * w + ch - '0';
return f * w;
}
//=============================================================
int main() {
int T = read();
while (T --) {
scanf("%s", s + 1);
int n = strlen(s + 1), ans2 = 0;
int min = s[1], max = s[n];
if (min >= max) std::swap(min, max);
for (int i = 1; i <= n; ++ i) {
if (s[i] >= min && s[i] <= max) {
pos[s[i] - 'a'].push_back(i);
++ ans2;
}
}
printf("%d %d\n", max - min, ans2);
if (s[1] < s[n]) {
for (int i = s[1] - 'a'; i <= s[n] - 'a'; ++ i) {
for (int j = 0, lth = pos[i].size(); j < lth; ++ j) {
printf("%d ", pos[i][j]);
}
pos[i].clear();
}
} else {
for (int i = s[1] - 'a'; i >= s[n] - 'a'; -- i) {
for (int j = 0, lth = pos[i].size(); j < lth; ++ j) {
printf("%d ", pos[i][j]);
}
pos[i].clear();
}
}
printf("\n");
}
return 0;
}
D
\(t\) 组数据,每组数据给定整数 \(n\),两个长度为 \(n\) 的数列 \(x\),\(y\)。
现在有 \(n\) 个人,第 \(i\) 个人计划买 \(x_i\) 元的饭菜,有 \(y_i\) 元的预算。要求将这些人分成若干组,每组至少有 2 个人。同一组的人会一起点餐,并一起支付吃饭的费用。
问是否存在一种分组方案,使得每组的预算都能足够购买每个人所需的饭菜?若存在,最大化所分的组数并输出。
\(1\le t\le 10^4\),\(2\le n\le 10^5\),\(1\le x_i,y_i\le 10^9\),\(\sum n\le 10^5\)。
2S,256MB。
贪心。
对于第 \(i\) 个人,如果满足 \(x_i>y_i\),称这个人为白嫖怪,否则称他为富哥。
显然一组两人肯定优于一组更多人,且每组至少有一个富哥。如果第三人是富哥,他可以帮助更多的白嫖怪;如果第三人是白嫖怪,他不应当再拖累这一组的富哥。且每组两人更容易最大化所分组数。
于是考虑按每个人 \(y_i - x_i\) 的值排序,双指针挑选富哥和白嫖怪组合,若预算不够,选择 \(y_i - x_i\) 更大的白嫖怪即可。
//By:Luckyblock
/*
大学傻逼
a b c d e f g h i j k l m n o p q r s t u v w x y z
*/
#include <cstdio>
#include <cctype>
#include <algorithm>
const int kN = 1e5 + 10;
//=============================================================
int a[kN], x[kN], y[kN];
//=============================================================
int read() {
int f = 1, w = 0; char ch = getchar();
for (; !isdigit(ch); ch = getchar()) if (ch == '-') f = -1;
for (; isdigit(ch); ch = getchar()) w = 10 * w + ch - '0';
return f * w;
}
//=============================================================
int main() {
int T = read();
while (T --) {
int n = read(), ans = 0, l = 1, r = n;
for (int i = 1; i <= n; ++ i) x[i] = read();
for (int i = 1; i <= n; ++ i) y[i] = read();
for (int i = 1; i <= n; ++ i) a[i] = y[i] - x[i];
std::sort(a + 1, a + n + 1);
for (; l < r; ) {
while ((a[r] + a[l] < 0) && (l < r)) ++ l;
if (l < r) ++ ans, ++ l, -- r;
}
printf("%d\n", ans);
}
return 0;
}
E
交互,溜了。
F
\(t\) 组数据,每组数据给定一仅由 \(0\sim 9\) 构成的字符串 \(s\),定义 \(v(l,r)\) 为 \(s\) 第 \(l\) 到第 \(r\) 位,忽略前导零后构成的整数的值。
给定整数 \(w,m\),接下来给出 \(m\) 次询问,第 \(i\) 次询问给定参数 \(l_i,r_i,k_i\),要求找到两个整数 \(L_1,L_2\),满足:
- \(1\le L_1,L_2\le |s| - w + 1\),且\(L_i\not = L_2\)。
- \(v(L_1,L_1 + w - 1) \times v(l,r) + v(L_2, L_2 + w - 1)\equiv k_i \pmod 9\)
- 如果有多组解,输出 \(L_1\) 最小的;\(L_1\) 相同则输出 \(L_2\) 最小的。
\(1\le t\le 10^4\),\(2\le n\le 2\times 10^5\),\(1\le l_i\le r_i\le n\),\(0\le k_i\le 8\),\(\sum n\le 2\times 10^5\)。
3S,256MB。
类似哈希的玩意。
考虑预处理字符串前缀 \(v\) 的值的 \(10\) 的幂,以类似哈希的方法 \(O(1)\) 得到某一子串对应的 \(v\),然后预处理所有长度为 \(w\) 的子串的 \(v\)。
然后调整一下式子:
预处理两个数组:与 \(x\) 相乘模 \(9\) 为 \(y\) 的长度为 \(w\) 的子串的左端点的最小值与次小值、模 \(9\) 为 \(y\) 的长度为 \(w\) 的子串的左端点的最小值与次小值,即代码中的 \(\operatorname{pos_1}\) 与 \(\operatorname{pos_2}\)。回答询问时枚举模 \(9\) 的余数,用预处理的值回答即可,注意令 \(L_1\not= L_2\) 的处理。
总复杂度是 \(O(n + m)\) 级别,常数略大,但是 3S。
//By:Luckyblock
/*
*/
#include <cstdio>
#include <cctype>
#include <cstring>
#include <algorithm>
const int kN = 2e5 + 10;
//=============================================================
int n, m, w;
int pow10[kN], h[kN], pos1[9][9][2], pos2[9][2];
char s[kN];
//=============================================================
inline int read() {
int f = 1, w = 0; char ch = getchar();
for (; !isdigit(ch); ch = getchar()) if (ch == '-') f = - 1;
for (; isdigit(ch); ch = getchar()) w = (w << 3) + (w << 1) + ch - '0';
return f * w;
}
int Val(int l_, int r_) {
return ((h[r_] - h[l_ - 1] * pow10[w] + 9) % 9 + 9) % 9;
}
void Init() {
pow10[0] = 1;
for (int i = 0; i < 9; ++ i) {
pos2[i][0] = pos2[i][1] = 0;
for (int j = 0; j < 9; ++ j) pos1[i][j][0] = pos1[i][j][1] = 0;
}
n = strlen(s + 1);
for (int i = 1; i <= n; ++ i) {
pow10[i] = pow10[i - 1] * 10 % 9;
h[i] = (h[i - 1] * 10 + s[i] - '0') % 9;
}
for (int i = w; i <= n; ++ i) {
int val = Val(i - w + 1, i);
if (!pos2[val][0]) pos2[val][0] = i - w + 1;
else if (!pos2[val][1]) pos2[val][1] = i - w + 1;
for (int j = 0; j < 9; ++ j) {
if (!pos1[j * val % 9][j][0]) pos1[j * val % 9][j][0] = i - w + 1;
else if (!pos1[j * val % 9][j][1]) pos1[j * val % 9][j][1] = i - w + 1;
}
}
}
void Query(int l_, int r_, int k_) {
int val = Val(l_, r_);
int ans1 = n + 1, ans2 = n + 1;
for (int i = 0; i < 9; ++ i) {
int x = (k_ - i + 9) % 9;
for (int j = 0; j <= 1; ++ j) {
int l1 = pos1[i][val][j];
int l2 = ((pos2[x][0] == l1) ? pos2[x][1] : pos2[x][0]);
if (l1 && l2) {
if (l1 < ans1) ans1 = l1, ans2 = l2;
else if (l1 == ans1 && l2 < ans2) ans2 = l2;
}
}
}
if (ans1 == n + 1) ans1 = ans2 = -1;
printf("%d %d\n", ans1, ans2);
}
//=============================================================
int main() {
int t = read();
while (t --) {
scanf("%s", s + 1);
w = read(), m = read();
Init();
while (m --) {
int l = read(), r = read(), k = read();
Query(l, r, k);
}
}
return 0;
}
G
好像很牛逼,之后再说。
写在最后
- 从自己的博客学会了 hash。