蓝桥杯 2022年省赛真题
Java 大学C组

  • 试题 A: 排列字母
  • 试题 B: 特殊时间
  • 试题 C: 纸张尺寸
  • 试题 D: 求和
  • 试题 E: 矩形拼接
  • 试题 F: 选数异或
  • 试题 G: GCD
  • 试题 H: 青蛙过河
  • 试题  I: 因数平方和
  • 试题 J: 最长不下降子序列

  很难避免,需要在错误中成长的困境。

  毕竟人都是向下望就变得能坚强,

  我亦尔耳。


试题 A: 排列字母

本题总分:555 分


【问题描述】

  小蓝要把一个字符串中的字母按其在字母表中的顺序排列。

  例如,LANQIAO\mathrm{LANQIAO}LANQIAO 排列后为 AAILNOQ\mathrm{AAILNOQ}AAILNOQ。

  又如,GOODGOODSTUDYDAYDAYUP\mathrm{GOODGOODSTUDYDAYDAYUP}GOODGOODSTUDYDAYDAYUP 排列后为 AADDDDDGGOOOOPSTUUYYY\mathrm{AADDDDDGGOOOOPSTUUYYY}AADDDDDGGOOOOPSTUUYYY。

  请问对于以下字符串,排列之后字符串是什么?

  WHERETHEREISAWILLTHEREISAWAY\mathrm{WHERETHEREISAWILLTHEREISAWAY}WHERETHEREISAWILLTHEREISAWAY

【答案提交】

  这是一道结果填空的题,你只需要算出结果后提交即可。本题的结果为一个由大写字母组成的字符串,在提交答案时只填写这个字符串,填写多余的内容将无法得分。


AAAEEEEEEHHHIIILLRRRSSTTWWWY


public class Test {public static void main(String[] args) {java.util.Arrays.stream("WHERETHEREISAWILLTHEREISAWAY".split("")).sorted().forEach(System.out::print);}
}

  整个活。


试题 B: 特殊时间

本题总分:555 分


【问题描述】

  202220222022 年 222 月 222222 日 222222:202020 是一个很有意义的时间,年份为 202220222022,由 333 个 222 和 111 个 000 组成,如果将月和日写成 444 位,为 022202220222,也是由 333 个 222 和 111 个 000 组成,如果将时间中的时和分写成 444 位,还是由 333 个 222 和 111 个 000 组成。

  小蓝对这样的时间很感兴趣,他还找到了其它类似的例子,比如 111111111 年 101010 月 111111 日 010101:111111,220222022202 年 222 月 222222 日 222222:020202 等等。

  请问,总共有多少个时间是这种年份写成 444 位、月日写成 444 位、时间写成 444 位后由 333 个一种数字和 111 个另一种数字组成。注意 111111111111 年 111111 月 111111 日 111111:111111 不算,因为它里面没有两种数字。

【答案提交】

  这是一道结果填空的题,你只需要算出结果后提交即可。本题的结果为一个整数,在提交答案时只填写这个整数,填写多余的内容将无法得分。


212


  在 333 个相同的个位数中插入 111 个个位数,显然可以组成 444 个不同的数字(不一定是 444 位数),于是我们可以另一个合法的 月日时分 与 444 个不同的年份组成映射关系,只要统计出合法的 日月时分 个数,将其乘上一个 444,答案就被计算出来了。

import java.time.format.DateTimeFormatter;
import java.time.LocalDateTime;public class Test {public static void main(String[] args) { new Test().run(); }void run() {DateTimeFormatter date = DateTimeFormatter.ofPattern("MMdd");DateTimeFormatter time = DateTimeFormatter.ofPattern("HHmm");LocalDateTime start = LocalDateTime.of(0000, 01, 01, 00, 00);LocalDateTime end = LocalDateTime.of(0000, 12, 31, 23, 59);int[] buff = new int[128];int ans = 0;for (; start.compareTo(end) <= 0; start = start.plusMinutes(1)) {for (char i = '0'; i <= '9'; ++i) buff[i] = 0;for (byte b : start.format(date).getBytes()) ++buff[b];boolean flag1 = true, flag3 = true;for (char i = '0'; i <= '9'; ++i)if (buff[i] == 1) flag1 = false;else if (buff[i] == 3) flag3 = false;if (flag1 || flag3) continue;for (byte b : start.format(time).getBytes()) --buff[b];for (char i = '0'; i <= '9'; ++i)if (buff[i] != 0) flag1 = true;if (!flag1) ++ans;}System.out.println(4 * ans);}
}

试题 C: 纸张尺寸

时间限制: 1.0s1.0\mathrm s1.0s 内存限制: 512.0MB512.0\mathrm{MB}512.0MB 本题总分:101010 分


【问题描述】

  在 ISO\mathrm{ISO}ISO 国际标准中定义了 A0\mathrm A0A0 纸张的大小为 1189mm×841mm1189\mathrm{mm} × 841\mathrm{mm}1189mm×841mm,将 A0\mathrm A0A0 纸沿长边对折后为 A1\mathrm A1A1 纸,大小为 841mm×594mm841\mathrm{mm} × 594\mathrm{mm}841mm×594mm,在对折的过程中长度直接取下整(实际裁剪时可能有损耗)。将 A1\mathrm A1A1 纸沿长边对折后为 A2\mathrm A2A2 纸,依此类推。

  输入纸张的名称,请输出纸张的大小。

【输入格式】

  输入一行包含一个字符串表示纸张的名称,该名称一定是 A0\mathrm A0A0、A1\mathrm A1A1、A2\mathrm A2A2、A3\mathrm A3A3、A4\mathrm A4A4、A5\mathrm A5A5、A6\mathrm A6A6、A7\mathrm A7A7、A8\mathrm A8A8、A9\mathrm A9A9 之一。

【输出格式】

  输出两行,每行包含一个整数,依次表示长边和短边的长度。

【样例输入 1】

A0

【样例输出 1】

1189
841

【样例输入 2】

A1

【样例输出 2】

841
594

  签到题,Java8\mathrm{Java}\ 8Java 8 支持 switch String 对象,

  直接用 switch 的特性写了。

import java.util.Scanner;public class Main {public static void main(String[] args) { new Main().run(); }int length = 1189, wide = 841;void cut() {int temp = wide;wide = length / 2;length = temp;}void run() {switch (new Scanner(System.in).next()) {case "A9": cut();case "A8": cut();case "A7": cut();case "A6": cut();case "A5": cut();case "A4": cut();case "A3": cut();case "A2": cut();case "A1": cut();default: System.out.printf("%d\n%d", length, wide);}}
}

试题 D: 求和

时间限制: 1.0s1.0\mathrm s1.0s 内存限制: 512.0MB512.0\mathrm{MB}512.0MB 本题总分:101010 分


【问题描述】

  给定 nnn 个整数 a1,a2,⋯,ana_1, a_2, \cdots , a_na1​,a2​,⋯,an​,求它们两两相乘再相加的和,即S=a1⋅a2+a1⋅a3+⋯+a1⋅an+a2⋅a3+⋯+an−2⋅an−1+an−2⋅an+an−1⋅an。S = a_1\cdot a_2 + a_1\cdot a_3 + \cdots + a_1\cdot a_n + a_2\cdot a_3 +\cdots + a_{n−2}\cdot a_{n−1} + a_{n−2}\cdot a_n + a_{n−1}\cdot a_n。S=a1​⋅a2​+a1​⋅a3​+⋯+a1​⋅an​+a2​⋅a3​+⋯+an−2​⋅an−1​+an−2​⋅an​+an−1​⋅an​。

【输入格式】

  输入的第一行包含一个整数 nnn 。

  第二行包含 nnn 个整数 a1,a2,⋯,ana_1, a_2,\cdots,a_na1​,a2​,⋯,an​。

【输出格式】

  输出一个整数 SSS,表示所求的和。请使用合适的数据类型进行运算。

【样例输入】

4
1 3 6 9

【样例输出】

117

【评测用例规模与约定】

  对于 30%30\%30% 的数据,1≤n≤1000,1≤ai≤1001 ≤ n ≤ 1000,1 ≤ a_i ≤ 1001≤n≤1000,1≤ai​≤100。
  对于所有评测用例,1≤n≤200000,1≤ai≤10001 ≤ n ≤ 200000,1 ≤ a_i ≤ 10001≤n≤200000,1≤ai​≤1000。


公式递推


  将 SSS 中包含 a1a_1a1​ 的项整理出来,有:

  S=a1⋅(a2+a3+⋯+an)+a2⋅a3+⋯+an−2⋅an−1+an−2⋅an+an−1⋅anS=a_1\cdot(a_2+a_3+\cdots+a_n)+ a_2\cdot a_3 +\cdots + a_{n−2}\cdot a_{n−1} + a_{n−2}\cdot a_n + a_{n−1}\cdot a_nS=a1​⋅(a2​+a3​+⋯+an​)+a2​⋅a3​+⋯+an−2​⋅an−1​+an−2​⋅an​+an−1​⋅an​

  同样的将余项中包含 a2a_2a2​、a3a_3a3​、⋯\cdots⋯、ana_nan​ 的项整理出来:

  S=a1⋅(a2+a3+⋯+an)+a2⋅(a3+a4+⋯+an)+⋯+an−1⋅an=a1⋅∑i=2nai+a2⋅∑i=3nai+⋯+an−1⋅∑i=nnai\begin{aligned}S&=a_1\cdot(a_2+a_3+\cdots+a_n)+ a_2\cdot(a_3+a_4+\cdots+a_n)+\cdots+a_{n-1}\cdot a_n\\&=a_1\cdot\sum_{i=2}^na_i+a_2\cdot \sum_{i=3}^na_i+\cdots+a_{n-1}\cdot\sum_{i=n}^{n}a_i\end{aligned}S​=a1​⋅(a2​+a3​+⋯+an​)+a2​⋅(a3​+a4​+⋯+an​)+⋯+an−1​⋅an​=a1​⋅i=2∑n​ai​+a2​⋅i=3∑n​ai​+⋯+an−1​⋅i=n∑n​ai​​

  也就 SSS 等于每个元素乘以右边所有元素的和的和,考虑到实现计算的代码量,我们将 SSS 的项重排,重新整理出:

  S=a1⋅∑i=10ai+a2⋅∑i=11ai+⋯+an⋅∑i=1n−1aiS=a_1\cdot\displaystyle\sum_{i=1}^{0}a_i+a_2\cdot \sum_{i=1}^{1}a_i+\cdots+a_{n}\cdot\sum_{i=1}^{n-1}a_iS=a1​⋅i=1∑0​ai​+a2​⋅i=1∑1​ai​+⋯+an​⋅i=1∑n−1​ai​

import java.io.StreamTokenizer;
import java.io.InputStreamReader;
import java.io.BufferedReader;
import java.io.IOException;public class Main {public static void main(String[] args) { new Main().run(); }void run() {int n = nextInt(), sum = 0;long a, ans = 0;while(n-- > 0) {a = nextInt();ans += a * sum;sum += a;}System.out.println(ans);}StreamTokenizer in = new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in)));int nextInt() {try {in.nextToken();} catch (IOException e) {e.printStackTrace();}return (int)in.nval;}
}

试题 E: 矩形拼接

时间限制: 1.0s1.0\mathrm s1.0s 内存限制: 512.0MB512.0\mathrm{MB}512.0MB 本题总分:151515 分


【问题描述】

  已知 333 个矩形的大小依次是 a1×b1a_1 × b_1a1​×b1​、a2×b2a_2 × b_2a2​×b2​ 和 a3×b3a_3 × b_3a3​×b3​。用这 333 个矩形能拼出的所有多边形中,边数最少可以是多少?

  例如用 3×23 × 23×2 的矩形(用 A\mathrm AA 表示)、4×14 × 14×1 的矩形(用 B\mathrm BB 表示)和 2×42 × 42×4 的矩形(用 C\mathrm CC 表示)可以拼出如下 444 边形。


  例如用 3×23 × 23×2 的矩形(用 A\mathrm AA 表示)、3×13 × 13×1 的矩形(用 B\mathrm BB 表示)和 1×11 × 11×1 的矩形(用 C\mathrm CC 表示)可以拼出如下 666 边形。

【输入格式】

  输入包含多组数据。

  第一行包含一个整数 TTT,代表数据组数。

  以下 TTT 行,每行包含 666 个整数 a1,b1,a2,b2,a3,b3a_1, b_1, a_2, b_2, a_3, b_3a1​,b1​,a2​,b2​,a3​,b3​,其中 a1,b1a_1, b_1a1​,b1​ 是第一个矩形的边长,a2,b2a_2, b_2a2​,b2​ 是第二个矩形的边长,a3,b3a_3, b_3a3​,b3​ 是第三个矩形的边长。

【输出格式】

  对于每组数据,输出一个整数代表答案。

【样例输入】

2
2 3 4 1 2 4
1 2 3 4 5 6

【样例输出】

4
8

【评测用例规模与约定】

  对于 10%10\%10% 的评测用例,1≤T≤5,1≤a1,b1,a2,b2,a3,b3≤10,a1=a2=a31 ≤ T ≤ 5,1 ≤ a_1, b_1, a_2, b_2, a_3, b_3 ≤ 10,a_1 = a_2 =a_31≤T≤5,1≤a1​,b1​,a2​,b2​,a3​,b3​≤10,a1​=a2​=a3​。
  对于 30%30\%30% 的评测用例,1≤T≤5,1≤a1,b1,a2,b2,a3,b3≤101 ≤ T ≤ 5,1 ≤ a_1, b_1, a_2, b_2, a_3, b_3 ≤ 101≤T≤5,1≤a1​,b1​,a2​,b2​,a3​,b3​≤10。
  对于 60%60\%60% 的评测用例,1≤T≤10,1≤a1,b1,a2,b2,a3,b3≤201 ≤ T ≤ 10,1 ≤ a_1, b_1, a_2, b_2, a_3, b_3 ≤ 201≤T≤10,1≤a1​,b1​,a2​,b2​,a3​,b3​≤20。
  对于所有评测用例,1≤T≤1000,1≤a1,b1,a2,b2,a3,b3≤1001 ≤ T ≤ 1000,1 ≤ a_1, b_1, a_2, b_2, a_3, b_3 ≤ 1001≤T≤1000,1≤a1​,b1​,a2​,b2​,a3​,b3​≤100。


分类讨论


// 因为是分类讨论,所以不想写了。

试题 F: 选数异或

时间限制: 1.0s1.0\mathrm s1.0s 内存限制: 512.0MB512.0\mathrm{MB}512.0MB 本题总分:151515 分


【问题描述】

  给定一个长度为 nnn 的数列 A1,A2,⋯,AnA_1, A_2, \cdots , A_nA1​,A2​,⋯,An​ 和一个非负整数 xxx,给定 mmm 次查询, 每次询问能否从某个区间 [l,r][l,r][l,r] 中选择两个数使得他们的异或等于 xxx。

【输入格式】

  输入的第一行包含三个整数 n,m,xn, m, xn,m,x。

  第二行包含 nnn 个整数 A1,A2,⋯,AnA_1, A_2,\cdots, A_nA1​,A2​,⋯,An​。

  接下来 mmm 行,每行包含两个整数 li,ril_i,r_ili​,ri​ 表示询问区间 [li,ri][l_i,r_i][li​,ri​]。

【输出格式】

  对于每个询问, 如果该区间内存在两个数的异或为 xxx 则输出 yes\mathrm{yes}yes, 否则输出 no\mathrm{no}no。

【样例输入】

4 4 1
1 2 3 4
1 4
1 2
2 3
3 3

【样例输出】

yes
no
yes
no

【样例说明】
  显然整个数列中只有 2,32, 32,3 的异或为 111。

【评测用例规模与约定】

  对于 20%20\%20% 的评测用例,1≤n,m≤1001 ≤ n, m ≤ 1001≤n,m≤100;
  对于 40%40\%40% 的评测用例,1≤n,m≤10001 ≤ n, m ≤ 10001≤n,m≤1000;
  对于所有评测用例,1≤n,m≤100000,0≤x<220,1≤li≤ri≤n,0≤Ai<2201 ≤ n, m ≤ 100000 ,0 ≤ x < 2^{20} ,1 ≤ l_i ≤ r_i ≤ n ,0 ≤ A_i < 2^{20}1≤n,m≤100000,0≤x<220,1≤li​≤ri​≤n,0≤Ai​<220。


莫队分块


  对于 [li,ri][l_i,r_i][li​,ri​] 中的某个数 AkA_kAk​,若其中还存在一个数 AgA_gAg​ 使得 Ak⊕Ag=xA_k \oplus A_g = xAk​⊕Ag​=x,根据异或自反性质有:Ak⊕Ag⊕Ak=x⊕AkAg=x⊕Ak\begin{aligned}A_k \oplus A_g \oplus A_k&=x \oplus A_k\\A_g&=x \oplus A_k\end{aligned}Ak​⊕Ag​⊕Ak​Ag​​=x⊕Ak​=x⊕Ak​​  容易想到离线莫队来处理这个问题,具体地说:

  我们声明一个整形 cnt\mathrm{cnt}cnt 并建立一个 map\mathrm{map}map,一开始它存储了 [0,0][0,0][0,0] 间每个数出现的次数,对于每次往区间里面加入一个数 AkA_kAk​,将 cnt\mathrm{cnt}cnt 加上一个 AgA_gAg​ 出现次数,每次减去一个数 AkA_kAk​,将 cnt\mathrm{cnt}cnt 减去一个 AgA_gAg​ 出现次数,直至区间变为 [li,ri][l_i,r_i][li​,ri​],

  此时,根据 cnt\mathrm{cnt}cnt 是否大于 111 来回答是否存在题目所述的两个数。

import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.io.*;public class Main {public static void main(String[] args) { new Main().run(); }int block, x, cnt = 0;Map<Integer, Integer> map = new HashMap();void run() {PrintWriter out = new PrintWriter(System.out);int n = nextInt(), m = nextInt();boolean[] ans = new boolean[m];Query[] querys = new Query[m];block = (int)Math.sqrt(n);int[] A = new int[n + 1];x = nextInt();for (int i = 1; i <= n; ++i) A[i] = nextInt();for (int i = 0; i < n; ++i)querys[i] = new Query(i, nextInt(), nextInt());Arrays.sort(querys);int l = 1, r = 0;for (int i = 0; i < m; ++i) {while (querys[i].r > r) add(A[++r]);while (querys[i].l < l) add(A[--l]);while (querys[i].r < r) move(A[r--]);while (querys[i].l > l) move(A[l++]);ans[querys[i].idx] = cnt > 0;}for (int i = 0; i < m; ++i)out.println(ans[i] ? "yes" : "no");out.flush();}void add(int a) {if (map.containsKey(a ^ x))cnt += map.get(a ^ x);map.put(a, map.getOrDefault(a, 0) + 1);}void move(int a) {if (map.containsKey(a ^ x))cnt -= map.get(a ^ x);int tmp = map.get(a);if (tmp == 1) map.remove(a);else map.put(a, tmp - 1);}class Query implements Comparable<Query> {int l, r, idx;Query(int idx, int l, int r) {this.idx = idx;this.l = l;this.r = r;}@Overridepublic int compareTo(Query q) {return this.l / block == q.l / block ? ((this.l / block) % 2 == 0 ? this.r - q.r : q.r - this.r) : this.l - q.l;}}StreamTokenizer in = new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in)));int nextInt() {try {in.nextToken();} catch (IOException e) {e.printStackTrace();}return (int)in.nval;}
}

动态规划


  但其实可以预处理出 frf_rfr​,其意义为 [fr,r][f_r,r][fr​,r] 中存在一对 kkk、ggg ,fr≤k≤g≤rf_r \leq k \leq g \leq rfr​≤k≤g≤r 使得 Ak⊕Ag=xA_k \oplus A_g =xAk​⊕Ag​=x 且 frf_rfr​ 最大,若不存在则另 fr=0f_r = 0fr​=0。

  对于每一次询问 [li,ri][l_i,r_i][li​,ri​],我们只需判断 lilili 和 frif_{r_i}fri​​ 的大小关系就能确定 [li,ri][l_i,r_i][li​,ri​] 之间是否存在两个数,它们的异或等于 xxx。

  现在考虑转移,对于每一个 rrr,我们判断下标大于 rrr 的元素中是否存在 Ar⊕xA_r \oplus xAr​⊕x,其中最靠 rrr 的是 frf_rfr​ 的一个候选值,同时我们还要考虑 [fr−1,r−1][f_{r-1},r-1][fr−1​,r−1] 中是否有更优的方案,最终有状态转移方程:fr=max⁡{fr−1,max⁡{i∣Ai=Ar⊕x}}f_r=\max\{f_{r-1},\max\{i|A_i=A_r\oplus x\}\}fr​=max{fr−1​,max{i∣Ai​=Ar​⊕x}}

import java.io.*;public class Main {public static void main(String[] args) { new Main().run(); }void run() {PrintWriter out = new PrintWriter(System.out);int n = nextInt(), m = nextInt(), x = nextInt();int[] map = new int[1 << 20];int[] f = new int[n + 1];for (int r = 1; r <= n; ++r) {int a = nextInt();f[r] = max(f[r - 1], map[a ^ x]);map[a] = r;}for (int i = 0; i < m; ++i) {int l = nextInt();int r = nextInt();out.println(f[r] >= l ? "yes" : "no");}out.flush();}int max(int arg1, int arg2) { return arg1 > arg2 ? arg1 : arg2; }StreamTokenizer in = new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in)));int nextInt() {try {in.nextToken();} catch (IOException e) {e.printStackTrace();}return (int)in.nval;}
}

试题 G: GCD

时间限制: 1.0s1.0\mathrm s1.0s 内存限制: 512.0MB512.0\mathrm{MB}512.0MB 本题总分:202020 分


【问题描述】

  给定两个不同的正整数 a,ba, ba,b,求一个正整数 kkk 使得 gcd⁡(a+k,b+k)\gcd(a + k, b + k)gcd(a+k,b+k) 尽可能大,其中 gcd⁡(a,b)\gcd(a, b)gcd(a,b) 表示 aaa 和 bbb 的最大公约数,如果存在多个 kkk,请输出所有满足条件的 kkk 中最小的那个。

【输入格式】

  输入一行包含两个正整数 a,ba, ba,b,用一个空格分隔。

【输出格式】

  输出一行包含一个正整数 kkk。

【样例输入】

5 7

【样例输出】

1

【评测用例规模与约定】

  对于 20%20\%20% 的评测用例,a<b≤105a < b ≤ 10^5a<b≤105;
  对于 40%40\%40% 的评测用例,a<b≤109a < b ≤ 10^9a<b≤109;
  对于所有评测用例,1≤a<b≤10181 ≤ a < b ≤ 10^{18}1≤a<b≤1018。


更相减损术


  更相减损术告诉我们,对于整数 a,ba,ba,b,若 a≥ba\geq ba≥b,都有 gcd⁡(a,b)=gcd⁡(a−b,b)=gcd⁡(a,a−b)\gcd(a,b) = \gcd(a-b,b) = \gcd(a,a-b)gcd(a,b)=gcd(a−b,b)=gcd(a,a−b)。

  将 kkk 代入到式中,我们的目标就是使 gcd⁡(a+k,a−b)\gcd(a+k,a-b)gcd(a+k,a−b)、gcd⁡(b+k,a−b)\gcd(b+k,a-b)gcd(b+k,a−b) 最大。

  显然最大为 ∣a−b∣|a-b|∣a−b∣,此时 kkk 取 min⁡{−a,−b}(mod∣a−b∣)\min\{-a,-b\} \pmod{|a-b|}min{−a,−b}(mod∣a−b∣) 最小。

import java.util.StringTokenizer;
import java.io.InputStreamReader;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;public class Main {public static void main(String[] args) { new Main().run(); }void run() {InputReader in = new InputReader(System.in);long a = in.nextLong(), b = in.nextLong();long c = abs(a - b);if (c == 0 || c == 1) System.out.print("1");else System.out.print(min((-a % c + c) % c, (-b % c + c) % c));}long min(long arg1, long arg2) { return arg1 < arg2 ? arg1 : arg2; }long abs(long arg) { return arg > 0 ? arg : -arg; }class InputReader {BufferedReader reader;StringTokenizer token;InputReader(InputStream in) { this.reader = new BufferedReader(new InputStreamReader(in)); }String next() {if (token == null || !token.hasMoreTokens()) {try {token = new StringTokenizer(reader.readLine());} catch (IOException e) {e.printStackTrace();}}return token.nextToken();}long nextLong() { return Long.parseLong(next()); }}
}

  表达式写出来后,

  感觉怪怪的。。。


试题 H: 青蛙过河

时间限制: 1.0s1.0\mathrm s1.0s 内存限制: 512.0MB512.0\mathrm{MB}512.0MB 本题总分:202020 分


【问题描述】

  小青蛙住在一条河边,它想到河对岸的学校去学习。小青蛙打算经过河里的石头跳到对岸。

  河里的石头排成了一条直线,小青蛙每次跳跃必须落在一块石头或者岸上。不过,每块石头有一个高度,每次小青蛙从一块石头起跳,这块石头的高度就会下降 111,当石头的高度下降到 000 时小青蛙不能再跳到这块石头上(((某次跳跃后使石头高度下降到 000 是允许的)))。

  小青蛙一共需要去学校上 xxx 天课,所以它需要往返 2x2x2x 次。当小青蛙具有一个跳跃能力 yyy 时,它能跳不超过 yyy 的距离。

  请问小青蛙的跳跃能力至少是多少才能用这些石头上完 xxx 次课。

【输入格式】

  输入的第一行包含两个整数 n,xn, xn,x,分别表示河的宽度和小青蛙需要去学校的天数。请注意 2x2x2x 才是实际过河的次数。

  第二行包含 n−1n − 1n−1 个非负整数 H1,H2,⋯,Hn−1H_1, H_2,\cdots, H_{n−1}H1​,H2​,⋯,Hn−1​,其中 Hi>0H_i > 0Hi​>0 表示在河中与小青蛙的家相距 iii 的地方有一块高度为 HiH_iHi​ 的石头,Hi=0H_i = 0Hi​=0 表示这个位置没有石头。

【输出格式】

  输出一行,包含一个整数,表示小青蛙需要的最低跳跃能力。

【样例输入】

5 1
1 0 1 0

【样例输出】

4

【样例解释】
  由于只有两块高度为 111 的石头,所以往返只能各用一块。第 111 块石头和对岸的距离为 444,如果小青蛙的跳跃能力为 333 则无法满足要求。所以小青蛙最少需要 444 的跳跃能力。

【评测用例规模与约定】

  对于 30%30\%30% 的评测用例,n≤100n ≤ 100n≤100;
  对于 60%60\%60% 的评测用例,n≤1000n ≤ 1000n≤1000;
  对于所有评测用例,1≤n≤105,1≤x≤109,1≤Hi≤1041 ≤ n ≤ 10^5, 1 ≤ x ≤ 10^9, 1 ≤ H^i ≤ 10^41≤n≤105,1≤x≤109,1≤Hi≤104。


二分 + 贪心


  跳跃能力增加下,可以往返河的次数呈单调不下降,故考虑二分跳跃能力 mid\mathrm{mid}mid,转为跳跃能力在 mid\mathrm{mid}mid 下能否往返 2x2x2x 天的判断问题。

  首先一条从对岸到学校的有向路径取反后即为一条从学校到对岸的有向路径,故只需判断单向是否可达 2x2x2x 次,

  对于一条从 000 到 nnn 路径,位置在 iii 上的石头,若选择处于 jjj 处位置大于 iii 石头折返,路径数一定不大于先到 iii 再到 jjj,若选择从位置比 iii 小的 j′j'j′ 处跳跃到 iii,j′j'j′ 越小最终的路径数越大,因为较小的 j′j'j′ 会最先称为不可选方案。

  故考虑遍历每一个 iii,同时维护一个单调队列按序号存放石头,最开始里面存放着 H0=infH_0 = \mathrm{inf}H0​=inf,对于每一个 HiH_iHi​,我们先踢出位置小于 i−midi - \mathrm{mid}i−mid 的石头 ,新建一个临时石头 Hk=0H_k = 0Hk​=0,然后编号小到大的 HjH_jHj​,若 Hj≤Hi−HkH_j \leq H_i - H_kHj​≤Hi​−Hk​,则将 HkH_kHk​ 加上一个 HjH_jHj​,将 HjH_jHj​ 踢出队列,若 Hj>Hi−HkH_j > H_i - H_kHj​>Hi​−Hk​,则将 HjH_jHj​ 减去 Hi−HkH_i - H_kHi​−Hk​,重复此步直至队列为空或 Hi=HkH_i = H_kHi​=Hk​,最后将 HkH_kHk​ 入列,其编号为 iii,进入下一轮枚举,

  最终队列中的石头之和即是跳跃能力为 mid\mathrm{mid}mid 可走的次数。

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.StreamTokenizer;
import java.io.IOException;public class Main {public static void main(String[] args) { new Main().run(); }void run() {int n = nextInt(), x = nextInt() << 1;int[] H = new int[n + 1], S = new int[n + 1];long[] V = new long[n + 1];for (int i = 1; i < n; ++i) H[i] = nextInt();int mid, ans = 1, right = n;V[0] = Long.MAX_VALUE;while (ans < right) {mid = ans + right >> 1;int l = 0, r = 0;for (int i = 1; i <= n; ++i) {while (l <= r && S[l] < i - mid) ++l;if (H[i] > 0) {int Hk = 0;while (l <= r && Hk < H[i])if (V[l] <= H[i] - Hk) Hk += V[l++];else {V[l] -= H[i] - Hk; Hk = H[i];}if (Hk > 0) { S[++r] = i; V[r] = Hk; }}}long cnt = 0;while (l <= r)cnt += V[l++];if (cnt >= x) right = mid; else ans = mid + 1;}System.out.println(ans);}StreamTokenizer in = new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in)));int nextInt() {try {in.nextToken();} catch (IOException e) {e.printStackTrace();}return (int)in.nval;}
}

试题  I: 因数平方和

时间限制: 1.0s1.0\mathrm s1.0s 内存限制: 512.0MB512.0\mathrm{MB}512.0MB 本题总分:252525 分


【问题描述】

  记 f(x)f(x)f(x) 为 xxx 的所有因数的平方的和。例如:f(12)=12+22+32+42+62+122:f(12) = 1^2 + 2^2 + 3^2 + 4^2 + 6^2 +12^2:f(12)=12+22+32+42+62+122。

  定义 g(n)=∑i=1nf(i)g(n) = \sum^n_{i=1}f(i)g(n)=∑i=1n​f(i)。给定 nnn, 求 g(n)g(n)g(n) 除以 109+710^9 + 7109+7 的余数。

【输入格式】

  输入一行包含一个正整数 nnn。

【输出格式】

  输出一个整数表示答案 g(n)g(n)g(n) 除以 109+710^9 + 7109+7 的余数。

【样例输入】

100000

【样例输出】

680584257

【评测用例规模与约定】

  对于 20%20\%20% 的评测用例,n≤105n ≤ 10^5n≤105。
  对于 30%30\%30% 的评测用例,n≤107n ≤ 10^7n≤107。
  对于所有评测用例,1≤n≤1091 ≤ n ≤ 10^91≤n≤109。


数论分块


  根据倍数法求 [1,n][1,n][1,n] 中每个数的因数的完备性可得知:

  [1,n][1,n][1,n] 中因数有 111 的数有 ⌊n1⌋\lfloor\frac n1\rfloor⌊1n​⌋ 个;
  [1,n][1,n][1,n] 中因数有 222 的数有 ⌊n2⌋\lfloor\frac n2\rfloor⌊2n​⌋ 个;
  ⋯⋯\cdots\cdots⋯⋯
  [1,n][1,n][1,n] 中因数有 nnn 的数有 ⌊nn⌋\lfloor\frac nn\rfloor⌊nn​⌋ 个。

  于是我们可以将和式 g(n)=∑i=1nf(i)g(n) = \sum^n_{i=1}f(i)g(n)=∑i=1n​f(i) 演绎为

  g(n)=∑i=1n(⌊ni⌋i2)=∑l∣l∈unique{⌊ni⌋∣i∈[1,n]}(⌊n⌊nl⌋⌋−l+1)(l2+(l+1)2+⋯+⌊n⌊nl⌋⌋2)\begin{aligned}g(n) &= \sum_{i=1}^n(\lfloor\frac ni\rfloor i^2)\\&=\sum_{l|l\in\mathrm{unique\{\lfloor\frac ni\rfloor|i\in[1,n]\}}}\left(\left\lfloor\dfrac{\ n\ }{\lfloor\frac nl\rfloor}\right\rfloor-l+1\right)(l^2+(l+1)^2+\cdots+\left\lfloor\frac{\ n\ }{\lfloor\frac nl\rfloor}\right\rfloor^2)\end{aligned}g(n)​=i=1∑n​(⌊in​⌋i2)=l∣l∈unique{⌊in​⌋∣i∈[1,n]}∑​(⌊⌊ln​⌋ n ​⌋−l+1)(l2+(l+1)2+⋯+⌊⌊ln​⌋ n ​⌋2)​

  求解。

  容易想到使用数论分块将和式计算的复杂度优化至 O(n)O(\sqrt n)O(n​)。

import java.io.StreamTokenizer;
import java.io.InputStreamReader;
import java.io.BufferedReader;
import java.io.IOException;public class Main {public static void main(String[] args) { new Main().run(); }int p = 1000000007, inv6 = 166666668;void run() {int n = nextInt();long tmp, sum = 0, ans = 0;for (int l = 1, r; l <= n; l = r + 1) {r = n / (n / l);tmp = sum;sum = r * (r + 1L) % p * (2 * r + 1) % p * inv6 % p;ans = (ans + (n / l) * (sum - tmp) + p) % p;}System.out.println(ans);}StreamTokenizer in = new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in)));int nextInt() {try {in.nextToken();} catch (IOException e) {e.printStackTrace();}return (int)in.nval;}
}

试题 J: 最长不下降子序列

时间限制: 1.0s1.0\mathrm s1.0s 内存限制: 512.0MB512.0\mathrm{MB}512.0MB 本题总分:252525 分


【问题描述】

  给定一个长度为 NNN 的整数序列:A1,A2,⋯,AN:A_1, A_2,\cdots, A_N:A1​,A2​,⋯,AN​。现在你有一次机会,将其中连续的 KKK 个数修改成任意一个相同值。请你计算如何修改可以使修改后的数列的最长不下降子序列最长,请输出这个最长的长度。

  最长不下降子序列是指序列中的一个子序列,子序列中的每个数不小于在它之前的数。

【输入格式】

  输入第一行包含两个整数 NNN 和 KKK。

  第二行包含 NNN 个整数 A1,A2,⋯,ANA_1, A_2,\cdots, A_NA1​,A2​,⋯,AN​。

【输出格式】

  输出一行包含一个整数表示答案。

【样例输入】

5 1
1 4 2 8 5

【样例输出】

4

【评测用例规模与约定】

  对于 20%20\%20% 的评测用例,1≤K≤N≤1001 ≤ K ≤ N ≤ 1001≤K≤N≤100;
  对于 30%30\%30% 的评测用例,1≤K≤N≤10001 ≤ K ≤ N ≤ 10001≤K≤N≤1000;
  对于 50%50\%50% 的评测用例,1≤K≤N≤100001 ≤ K ≤ N ≤ 100001≤K≤N≤10000;
  对于所有评测用例,1≤K≤N≤105,1≤Ai≤1061 ≤ K ≤ N ≤ 10^5,1 ≤ A_i ≤ 10^61≤K≤N≤105,1≤Ai​≤106。


动态规划


  首先逆序单调栈 dp\mathrm{dp}dp 出 fif_ifi​,其含义为以 AiA_iAi​ 结束的最长递增子序列,最长是多少,然后逆序单调栈 dp\mathrm{dp}dp A[i,n]A[i,n]A[i,n] 中的最长递增子序列,此时栈中自底向上低 jjj 个元素表示长度为 jjj 的递增子序列,最后一个元素最小可以为多少,遍历的每一个 iii,我们在栈中二分查找最后一个不大于 Ai−kA_{i-k}Ai−k​ 的元素位置 jjj,并尝试用 fi−k+j+kf_{i-k}+j+kfi−k​+j+k 更新答案。

  为了加强代码的重用性,实现时可能与上面描述的不同,

  但我懒得改字,将就看。

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.StreamTokenizer;
import java.io.IOException;public class Main {public static void main(String[] args) { new Main().run(); }void run() {int n = nextInt(), k = nextInt(), top = 1, ans = 0;int[] S = new int[n + 5];int[] f = new int[n];int[] A = new int[n];for (int i = 0; i < n - k; ++i) {int g = upperBound(S, 1, top, A[i] = nextInt());if (g == top) S[++top] = 0x3F3F3F3F;S[g] = A[i];f[i] = g;}for (int i = n - k; i < n; ++i) A[i] = nextInt();for (top = 0; --n >= k;) {ans = max(ans, f[n - k] + lowerBound(S, 0, top, -A[n - k]));int g = upperBound(S, 0, top, -A[n]);if (g == top) S[++top] = 0;S[g] = -A[n];}System.out.println(max(ans, top) + k);}int upperBound(int[] A, int l, int r, int val) {while (l < r) {int mid = l + r >> 1;if (A[mid] <= val) l = mid + 1; else r = mid;}return l;}int lowerBound(int[] A, int l, int r, int val) {while (l < r) {int mid = l + r >> 1;if (A[mid] >= val) r = mid; else l = mid + 1;}return l;}int max(int arg1, int arg2) { return arg1 > arg2 ? arg1 : arg2; }StreamTokenizer in = new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in)));int nextInt() {try {in.nextToken();} catch (IOException e) {e.printStackTrace();}return (int)in.nval;}
}

第十三届蓝桥杯大赛软件赛省赛(Java 大学C组)相关推荐

  1. 2022年第十三届蓝桥杯大赛软件类决赛C/C++大学B组(国赛)题解

    2022国赛B组 题目一:C题 卡牌 题目二:D题 最大数字 题目三:E题 出差 题目四:F题 费用报销 题目五:G题 故障 题目六:H题 机房 题目七:I题 齿轮 题目八:J题 搬砖 刷题链接: h ...

  2. 2022年第十三届蓝桥杯大赛软件类决赛C/C++大学B组C题卡牌

    题意: 有n种牌,第i种牌有ai 张,一套牌为n张牌每种各一张,为凑出更多的牌,用m张空白牌写上种类,每种最多写bi张,求最多能凑几幅牌. 思考: 已知ai.bi,不考虑有多少张空白牌,最多能凑min ...

  3. 2022年第十三届蓝桥杯大赛软件类决赛C/C++大学B组E题出差

    题意: 有N个城市,编号1...N,无法从1出发到N,需要通过其他城市中转,并且到达后需隔离,求1到N时间最短的路线. 思路: 最短路变形,求时间最短,使用Dijkstra算法松弛时需加上隔离的时间, ...

  4. 2022年第十三届蓝桥杯大赛软件类国赛 C/C++ 大学B组

    试题 A: 2022 #include<bits/stdc++.h> using namespace std;typedef long long LL; LL dp[11][2023];i ...

  5. 第十三届蓝桥杯大赛软件类国赛 C/C++ 大学B组 试题 G: 故障

    试题 G: 故障 贝叶斯公式 条件概率的运用,即发生A事件的条件下,B事件发生的概率. #include<cstdio> #include<cmath> #include< ...

  6. 【第九届蓝桥杯大赛决赛真题】JAVA大学C组题解

    有空就会更新.... 有的题目重复了:再另一篇帖子:https://www.cnblogs.com/dgwblog/p/9090923.html 02 结果填空(满分29分) 标题:海盗与金币 12名 ...

  7. 2022年第十三届蓝桥杯大赛软件类决赛C/C++/Java/Python真题

    1.2022年第十三届蓝桥杯大赛软件类决赛C/C++大学A组真题 2022年第十三届蓝桥杯大赛软件类决赛C/C++大学A组真题 - 题库 - C语言网 2. 2022年第十三届蓝桥杯大赛软件类决赛C/ ...

  8. 第十三届蓝桥杯大赛软件赛省赛真题

    第十三届蓝桥杯大赛软件赛省赛Java 大学 B 组 文章目录 第十三届蓝桥杯大赛软件赛省赛Java 大学 B 组 [考生须知] 试题 A: 星期计算 试题 B: 山 试题 C: 字符统计 试题 D: ...

  9. 第十三届蓝桥杯大赛软件赛省赛 Python 大学 B 组

    第十三届蓝桥杯大赛软件赛省赛 Python 大学 B 组 第十三届蓝桥杯大赛软件赛省赛 Python 大学 B 组 [考生须知] 考试开始后,选手首先下载题目,并使用考场现场公布的解压密码解压试 题. ...

  10. 第十三届蓝桥杯大赛软件赛省赛(b组c语言)

    试题 A: 九进制转十进制 本题总分:5 分 [问题描述] 九进制正整数 (2022)9 转换成十进制等于多少? 这道题没什么说的,2*9*9*9+0*9*9+2*9+2*1=1478: 试题 B: ...

最新文章

  1. ext store 数据修改_Go 数据存储篇(一):基于内存存储实现数据增删改查功能...
  2. SpringBoot入门篇之properties中定义user.name失效解决
  3. C# 离线使用nuget
  4. SQL Server2008 查找用户登录日志
  5. r如何查询mysql中的数据类型_MySQL-mysql中的数据类型
  6. datatable某一行第N列为空的时候删除某一行
  7. android学习笔记---发送状态栏通知
  8. javascript类功能代码集
  9. 内存越界访问保护 内存泄漏研究 未完待续
  10. 安装hmc会依赖bios时间吗_拯救者R7000 2020/GTX 1650 安装 ubuntu 20.04 双系统
  11. bigdecimal取小数部分_无限小数的本质
  12. 大作业---Android本地音乐播放器开发知识点19145120
  13. 三个月华为od工作感受:关于转正,身份和适合谁
  14. English trip V1 - 1.How Do You Feel Now? Teacher:Lamb Key:形容词(Adjectives)
  15. pycharm 2018永久破解激活补丁 附安装教程
  16. 调eclipse背景颜色(绿色为例)
  17. 移动端HTML开发(模仿京东移动端)
  18. JAVA微信公众号推送信息
  19. 可以设置时间的计时器
  20. VBoxGuestAdditions下载地址

热门文章

  1. 邮箱,手机号的正则表达式
  2. 红孩儿编辑器的模块设计13
  3. 通过js实现电灯开关案例
  4. 【实战】OpenCV+Python项目实战--信用卡数字识别
  5. Qlikview---日期字段
  6. 用矩形法求定积分,分别求sin x dx ,cos x dx,e exp x;
  7. UiAUtomator基础入门级学习
  8. 用opencv检测convexity defects
  9. 计算机科学与技术哪个方向好就业前景,【计算机科学与技术】专业就业前景以及未来的就业方向...
  10. 亚马逊云的服务器(EC2)+阿里云的域名,添加ssl证书