大家好,我是snippet,今天是刷蓝桥真题的第十六天,今天的题包含前缀和+单调队列(第三题)、带权并查集(第四题)的知识,第三题感觉是数据太多了导致的超时,今天的知识有点硬核,下面是我今天的题解
目录
一、英文字母
题目内容:
问题描述
输入一个正整数 n, 输出第 n 个大写英文字母。
输入格式
输入一行包含一个正整数 n 。
输出格式
输出一行包含一个字母。
样例输入 1
12
样例输出 1
L
样例输入 2
17
样例输出 2
Q
评测用例规模与约定
对于所有评测用例, 1≤n≤26 。
运行限制
- 最大运行时间:1s
- 最大运行内存: 256M
解题思路:
先输入数据n,把数据n+'A'-1,然后用字符类型输出数据
代码:
package 蓝桥杯31天真题冲刺.Day16;
import java.util.Scanner;
/**
* @author snippet
* @data 2023-03-19
* 英文字母-蓝桥云课
*/
public class T1_英文字母 {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
System.out.println((char)(n-1+'A'));
}
}
二、单词分析
题目内容:
题目描述
小蓝正在学习一门神奇的语言,这门语言中的单词都是由小写英文字母组 成,有些单词很长,远远超过正常英文单词的长度。小蓝学了很长时间也记不住一些单词,他准备不再完全记忆这些单词,而是根据单词中哪个字母出现得最多来分辨单词。
现在,请你帮助小蓝,给了一个单词后,帮助他找到出现最多的字母和这 个字母出现的次数。
输入描述
输入一行包含一个单词,单词只由小写英文字母组成。
对于所有的评测用例,输入的单词长度不超过 1000。
输出描述
输出两行,第一行包含一个英文字母,表示单词中出现得最多的字母是哪个。如果有多个字母出现的次数相等,输出字典序最小的那个。
第二行包含一个整数,表示出现得最多的那个字母在单词中出现的次数。
输入输出样例
示例 1
输入
lanqiao
输出
a
2
示例 2
输入
longlonglongistoolong
输出
o
6
运行限制
- 最大运行时间:1s
- 最大运行内存: 256M
解题思路:
用一个一维数组来存每个字符出现的次数,再用一个max来存出现次数最多的字符出现的次数,按字典序输出第一个出现次数为max的子符同时输出max的值
代码:
package 蓝桥杯31天真题冲刺.Day16;
import java.util.Scanner;
/**
* @author snippet
* @data 2023-03-19
* 单词分析-蓝桥云课
*/
public class T2_单词分析 {
static int[] arr = new int[26];
static int max;
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
String s = sc.nextLine();
for (int i = 0; i < s.length(); i++) {
int t = ++arr[s.charAt(i)-'a'];
max = Math.max(max, t);
}
for (int i = 0; i < arr.length; i++) {
if (arr[i] == max) {
System.out.println((char)(i+'a'));
System.out.println(max);
return;
}
}
}
}
三、火星旅行
题目内容:
题目描述
火星的所有空间站都位于一个圆上,Byteazar 在其中一个空间站登陆然后开始绕圈旅行。
旅行需要耗费油料,一升油料只能跑 1 米,每个空间站可以补给的油料都有所不同。
Byteazar 每到一个空间站便可以把该空间站的油料全部拿走(他的油箱是没有容量限制的)。但是如果走到某个时候突然没油了那么旅行便失败了。
Byteazar 需要决定要在哪个地方登陆使得他能顺利访问完所有的空间站后回到他当初登陆的地方,他登陆后可以选择两个方向中的任意一个进行旅行。
输入描述
第一行一个整数 n,代表空间站数量,所有空间站由 1 至 n 进行标号。
之后 n 行,每行两个整数 pi,di,第 i+1 行描述了第 i 号空间站的信息,其中 pi 表示该空间站可以补给的油量,di 则指明了它到 i+1 号空间站的距离,对于 n 号空间站,di 表示它和 1 号空间站的距离。
其中,3≤n≤10^6,pi≥0,di>0,∑di≤2×109。
输出描述
输出 n 行,每行一个字符串
TAK
或NIE
。若你认为在 i 号空间站登陆是可行的,则需要在第 i 行输出
TAK
,否则输出NIE
。输入输出样例
示例 1
输入
5
3 1
1 2
5 2
0 1
5 4
输出
TAK
NIE
TAK
NIE
TAK运行限制
- 最大运行时间:1s
- 最大运行内存: 128M
解题思路:
这个题可视为循环的空间站,只要每个空间站可以在够油的情况下通过一次就可以了,我们可以使用单调队列来对每个位置进行遍历并判断,在判断的时候 我们需要先求从第一个空间站到每个空间站的加油量-空间站的距离的值的和值,用来进行区间判断是否可以从k位置开始进(从k位置进,遍历每个位置,看补给油量是否大于距离)
在使用单调队列的时候我们可以使用数组滑窗或者双端队列来档滑窗都可以
可能是这个题的数据太多了,后面两个案例超时过不了
代码:
package 蓝桥杯31天真题冲刺.Day16;
import java.io.*;
import java.util.Deque;
import java.util.LinkedList;
/**
* @author snippet
* @data 2023-03-19
* 推导部分和-蓝桥云课
*/
// 前缀和+单调队列
// 单调队列 可以使用数组滑窗 或者 双端队列滑窗都可以
// 但是这个题数据太多了 JavaAC不了 后面两个数据会超时
public class T3_火星旅行 {
static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
static PrintWriter pw = new PrintWriter(new BufferedWriter(new OutputStreamWriter(System.out)));
static int n;// n表示空间站的个数
static int N = 1000100;
static int[] s = new int[N * 2];
static int[] p = new int[N];// 一维数组p存该空间站的加油量
static int[] d = new int[N];// 一维数组d存该空间站到下一空间站的距离 如果是最后一个位置则是到1号空间站的距离
static boolean[] ans = new boolean[N];// 一维数组ans存该位置是不是可以成为初始位置
static int[] q = new int[N * 2];// 数组滑窗
// static Deque<Integer> queue = new LinkedList<>();// 双端队列
public static void main(String[] args) throws IOException {
String[] str = br.readLine().split(" ");
n = Integer.parseInt(str[0]);
for (int i = 1; i <= n; i++) {
str = br.readLine().split(" ");
p[i] = Integer.parseInt(str[0]);
d[i] = Integer.parseInt(str[1]);
}
// s数组里面存该位置的油量与下一位置的距离的差值
for (int i = 1; i <= n; i++) {
s[i] = p[i] - d[i];
s[i + n] = s[i];
}
// 前缀和求1-->i位置的油量-距离的值的总和
for (int i = 1; i <= 2 * n; i++) {
s[i] += s[i-1];
}
// 队头为最小值
// hh 表示头 tt 表示尾
int hh = 0, tt = -1;
q[hh] = 2 * n + 1;
for (int i = 2 * n; i >= 1; i--) {
if (hh <= tt && q[hh] > i + n - 1) hh++;
if (i < n) {
if (s[q[hh]] - s[i-1] >= 0) ans[i] = true;
}
while (hh <= tt && s[q[tt]] >= s[i-1]) tt--;
q[++tt] = i-1;
}
// queue.addLast(2 * n + 1);
// for (int i = 2 * n; i >= 1; i--) {
// if (queue.size() > 0 && queue.getFirst() > i + n - 1) queue.removeFirst();
// if (i < n) {
// if (s[queue.getFirst()] - s[i - 1] >= 0) ans[i] = true;
// }
// while (queue.size() > 0 && s[queue.getLast()] >= s[i - 1]) queue.removeLast();
// queue.addLast(i - 1);
// }
d[0] = d[n];
for (int i = 1; i <= n; i++) {
s[i] = p[i] - d[i - 1];
s[i + n] = s[i];
}
for (int i = 1; i <= 2 * n; i++) {
s[i] += s[i - 1];
}
// 队头为最大值
// hh 表示头 tt 表示尾
hh = 0;
tt = -1;
q[hh] = 0;
for (int i = 0; i <= 2 * n - 1; i++) {
if (hh <= tt && q[hh] > i + n - 1) hh++;
if (i + 1 > n) {
if (s[i+1] - s[q[hh]] >= 0) ans[i - n + 1] = true;
}
while (hh <= tt && s[q[tt]] <= s[i+1]) tt--;
q[++tt] = i+1;
}
// queue.clear();
// queue.addLast(0);
// for (int i = 0; i <= 2 * n - 1; i++) {
// if (queue.size() > 0 && queue.getFirst() < i - n + 1) queue.removeFirst();
// if (i + 1 > n) {
// if (s[i + 1] - s[queue.getFirst()] >= 0) ans[i - n + 1] = true;
// }
// while (queue.size() > 0 && s[queue.getLast()] <= s[i + 1]) queue.removeLast();
// queue.addLast(i + 1);
// }
for (int i = 1; i <= n; i++) {
if (ans[i]) pw.println("TAK");
else pw.println("NIE");
}
pw.flush();
br.close();
}
}
四、推导部分和
题目链接:推导部分和 - 蓝桥云课 (lanqiao.cn)
题目内容:
问题描述
对于一个长度为 N 的整数数列 A1,A2,⋯AN, 小蓝想知道下标 l 到 r 的部 分和 ∑i=lr=Al+Al+1+⋯+Ar 是多少?
然而, 小蓝并不知道数列中每个数的值是多少, 他只知道它的 M 个部分和 的值。其中第 i 个部分和是下标 li 到 ri 的部分和 ∑j=liri=Ali+Ali+1+⋯+Ari, 值是 Si 。
输入格式
第一行包含 3 个整数 N、M 和 Q 。分别代表数组长度、已知的部分和数量 和询问的部分和数量。
接下来 M 行, 每行包含 3 个整数 li,ri,Si 。
接下来 Q 行, 每行包含 2 个整数 l 和 r, 代表一个小蓝想知道的部分和。
输出格式
对于每个询问, 输出一行包含一个整数表示答案。如果答案无法确定, 输出 UNKNOWN。
样例输入
5 3 3
1 5 15
4 5 9
2 3 5
1 5
1 3
1 2样例输出
15
6
UNKNOWN评测用例规模与约定
对于 10% 的评测用例, 1≤N,M,Q≤10,−100≤Si≤100 。
对于 20% 的评测用例, 1≤N,M,Q≤20,−1000≤Si≤1000 。
对于 30% 的评测用例, 1≤N,M,Q≤50,−10000≤Si≤10000 。
对于 40% 的评测用例, 1≤N,M,Q≤1000,−10^6≤Si≤10^6 。
对于 60% 的评测用例, 1≤N,M,Q≤10000,−10^9≤Si≤10^9 。
对于所有评测用例, 1≤N,M,Q≤10^5,−10^12≤Si≤10^12,1≤li≤ri≤N, 1≤l≤r≤N 。数据保证没有矛盾。
运行限制
- 最大运行时间:3s
- 最大运行内存: 512M
解题思路:
给定部分区间值求某些区间的连通的值,这也就是维护连通分量,我们可以使用带权并查集来维护连通分量,我直接套的带权并查集的板子;
这个题可以参考执梗大佬的题解:第十三届蓝桥杯JavaA组、C++A组省赛 J 题——推导部分和 (AC)_执 梗的博客-CSDN博客
代码:
package 蓝桥杯31天真题冲刺.Day16;
import java.io.*;
/**
* @author snippet
* @data 2023-03-19
* 推导部分和-蓝桥云课
*/
// 带权并查集
class UF {
long[] f, d;
public UF(int n) {
f = new long[n];
d = new long[n];
for (int i = 1; i < n; ++i) {
f[i] = i;
}
}
int find(int x) {
if (x == f[x]) return (int) f[x];
//先记录祖宗
int root = find((int) f[x]);
//加上父亲的距离
d[x] += d[(int) f[x]];
//指向祖宗
return (int) (f[x] = root);
}
boolean same(int x, int y) {
return find(x) == find(y);
}
// 数据存入
boolean merge(int u, int v, long w) {
int x = find(u);
int y = find(v);
if (x == y) return false;
f[x] = y;
d[x] = w + d[v] - d[u];
return true;
}
}
public class T4_推导部分和 {
static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
static PrintWriter pw = new PrintWriter(new BufferedWriter(new OutputStreamWriter(System.out)));
static int n,m,q;
static int l,r;
static long s;
public static void main(String[] args) throws IOException {
String[] str = br.readLine().split(" ");
n = Integer.parseInt(str[0]);
m = Integer.parseInt(str[1]);
q = Integer.parseInt(str[2]);
UF uf = new UF(n + 10);
// 数据存储
for (int i = 1; i <= m; i++) {
str = br.readLine().split(" ");
l = Integer.parseInt(str[0]);
r = Integer.parseInt(str[1]);
s = Long.parseLong(str[2]);
// 在前缀和求区间[l,r]的和值时,我们一般使用s[r]-s[l-1]
uf.merge(l-1, r, s);
}
for (int i = 1; i <= q; i++) {
str = br.readLine().split(" ");
l = Integer.parseInt(str[0]);
r = Integer.parseInt(str[1]);
if (!uf.same(l-1, r)) pw.println("UNKNOWN");
else pw.println(uf.d[l - 1] - uf.d[r]);
}
pw.flush();
br.close();
}
}