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\)

然后调整一下式子:

\[v(L_1,L_1 + w - 1) \times v(l,r) + \equiv k_i - v(L_2, L_2 + w - 1)\pmod 9 \]

预处理两个数组:与 \(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。
posted @ 2022-09-27 17:26  Luckyblock  阅读(31)  评论(0编辑  收藏  举报