前言
整体评价
这场比赛整体前面要简单了,至少前6题是这样,不过G题除外,费用流的话,好像属于竞赛范畴了。不过G题赛中除了点小问题,导致过的人很少。
这边先讲E-G题的思路和解题代码。
比赛链接: 蓝桥杯 第 4 场算法双周赛
E. 充能计划
Q: 能量宝石能给一个区间充电,但是区间的每一个格子,只能接受同类宝石一次,求最终区间的充电情况?
假设没有同类宝石只限一次的限定,其实就是很纯粹的差分问题。
那如何解决同类宝石的问题呢?
这就属于区间合并的范畴,所以大体的思路,同类分组,然后区间合并,最后整体做差分。
import java.io.*;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(new BufferedInputStream(System.in));
int n = sc.nextInt();
int s = sc.nextInt();
int q = sc.nextInt();
int[] arr = new int[s + 1];
for (int i = 1; i <= s; i++) {
arr[i] = sc.nextInt();
}
// 分组
Map<Integer, List<int[]>> hash = new HashMap<>();
for (int i = 0; i < q; i++) {
int id = sc.nextInt();
int x = sc.nextInt();
hash.computeIfAbsent(id, t -> new ArrayList<>()).add(new int[] {x, Math.min(n, x + arr[id] - 1)});
}
int[] diff = new int[n + 2];
for (Map.Entry<Integer, List<int[]>> kv: hash.entrySet()) {
List<int[]> lists = kv.getValue();
Collections.sort(lists, Comparator.comparing(x -> x[0]));
// 同一个分组进行区间合并(只剩不相交的区间)
Deque<int[]> stack = new ArrayDeque<>();
for (int[] e: lists) {
if (!stack.isEmpty() && stack.peek()[1] >= e[0]) {
int[] cur = stack.pop();
stack.push(new int[] {cur[0], Math.max(cur[1], e[1])});
} else {
stack.push(new int[] {e[0], e[1]});
}
}
// 做差分
while (!stack.isEmpty()) {
int[] cur = stack.pop();
diff[cur[0]]++;
diff[cur[1]+1]--;
}
}
// 利用差分数组还原
int[] res = new int[n + 1];
int acc = 0;
for (int i = 1; i <= n; i++) {
acc += diff[i];
res[i] = acc;
}
System.out.println(IntStream.range(1, n + 1).mapToObj(x -> String.valueOf(res[x])).collect(Collectors.joining(" ")));
}
}
F. 大风起兮
Q: 给一个序列和一系列操作,求每次操作后的中位数?
其实这题是Python的sortedlist爽题,不过如果你手头有名次树的板子,也这题也是非常的easy.
那这题如何解呢?
- 名次树
- 对顶堆,正向求解(延迟删除)
- 对顶堆,逆向求解
- 树状数组(支持kth查询)
这些都可以解
不过这边借助树状数组来求解,因为树状数组也支持kth的查找
当然也是有代价,即需要离散化,外加双向映射。
import java.io.*;
import java.util.*;
import java.util.stream.Collectors;
public class Main {
static
public class BIT {
int n;
int[] arr;
int level = 0;
public BIT(int n) {
this.n = n;
this.arr = new int[n + 1];
this.level = (int)(Math.log(n) / Math.log(2)) + 1;
}
void update(int p, int d) {
while (p <= n) {
arr[p] += d;
p += p &-p;
}
}
int query(int p) {
int res = 0;
while (p > 0) {
res += arr[p];
p -= p & -p;
}
return res;
}
int kth(int k) {
int res = 0;
for (int i = level; i >= 0; i--) {
int bin = 1 << i;
if (res + bin <= n && k > arr[res + bin]) {
k -= arr[res + bin];
res += bin;
}
}
return res + 1;
}
}
public static void main(String[] args) {
// 需要替换为快读
Scanner sc = new Scanner(new BufferedInputStream(System.in));
int n = sc.nextInt();
TreeSet<Integer> ts = new TreeSet<>();
int[] arr = new int[n + 1];
for (int i = 1; i <= n;i++) {
arr[i] = sc.nextInt();
ts.add(arr[i]);
}
// 离散化 + 双向映射
int ptr = 0;
Map<Integer, Integer> revMap = new HashMap<>();
Map<Integer, Integer> idMap = new HashMap<>();
for (Integer k: ts) {
idMap.put(k, ++ptr);
revMap.put(ptr, k);
}
BIT bit = new BIT(ptr);
for (int i = 1; i <= n; i++) {
bit.update(idMap.get(arr[i]), 1);
}
int q = sc.nextInt();
int m = n;
// 正向操作
List<Double> res = new ArrayList<>();
for (int i = 0; i < q; i++) {
int id = sc.nextInt();
bit.update(idMap.get(arr[id]), -1);
m--;
if (m % 2 == 1) {
double r = revMap.get(bit.kth((m + 1) / 2));
res.add(r);
} else {
int t1 = m / 2, t2 = m / 2 + 1;
double r1 = revMap.get(bit.kth(t1));
double r2 = revMap.get(bit.kth(t2));
res.add((r1 + r2) / 2.0);
}
}
System.out.println(res.stream().map(x -> String.format("%.1f", x)).collect(Collectors.joining(" ")));
}
}
最好的解法,还是逆序对顶堆
import java.io.BufferedInputStream;
import java.util.*;
import java.util.stream.Collectors;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(new BufferedInputStream(System.in));
int n = sc.nextInt();
int[] arr = new int[n + 1];
for (int i = 1; i <= n;i++) {
arr[i] = sc.nextInt();
}
int q = sc.nextInt();
Set<Integer> hash = new HashSet<>();
List<Integer> qs = new ArrayList<>();
for (int i = 0; i < q; i++) {
int id = sc.nextInt();
qs.add(id);
hash.add(id);
}
PriorityQueue<Integer> p1 = new PriorityQueue<>(Comparator.comparing(x -> x));
PriorityQueue<Integer> p2 = new PriorityQueue<>(Comparator.comparing(x -> -x));
for (int i = 1; i <= n; i++) {
if (!hash.contains(i)) {
p1.offer(arr[i]);
if (p1.size() > p2.size() + 1) {
p2.offer(p1.poll());
}
}
}
List<Double> res = new ArrayList<>();
for (int i = qs.size() - 1; i >= 0; i--) {
// 先查结果
if ((p1.size() + p2.size()) % 2 == 1) {
res.add(1.0 * p1.peek());
} else {
res.add((p1.peek() + p2.peek()) / 2.0);
}
// 再加回元素
int id = qs.get(i);
p1.offer(arr[id]);
if (p1.size() > p2.size() + 1) {
p2.offer(p1.poll());
}
}
// 逆转
Collections.reverse(res);
// 打印最后的结果
System.out.println(res.stream().map(x -> String.format("%.1f", x)).collect(Collectors.joining(" ")));
}
}
G. 时空追捕
后期在补上