A题
维卡和她的朋友们去商场购物,商场可以表示为一个由边长为 n n n 和 m m m 的房间组成的长方形网格。开始的时候维卡位于坐标 ( x x x, y y y), 他有 k k k个朋友,分别位于给定的坐标中(可能重复),每隔一分钟,维卡先移动到她选择的一个相邻房间,然后每个朋友(看到维卡的选择)也选择一个相邻房间移动,上下左右为相邻房间。如果分钟结束时(即所有女孩都移动到相邻房间后),至少有一个朋友与维卡在同一个房间,她就会被抓住。维卡能永远逃离她那些讨厌的朋友吗?能就输出“Yes”,否则输出“No”。
思路
- 如果存在一个朋友的坐标和(即 x + y x+y x+y)奇偶性与vika相同,则vika一定会被抓到
- 想象成国际象棋的棋盘,一个格子到另外一个格子的颜色一定不相同
- 在vika没有到达边界时,追vika的人与vika的距离是固定的(哈曼顿距离),当到达边界时,再移动二者的距离会越来越近,最终一定会被抓到
- 思路源于灵神
代码
public static void solve() throws IOException {
int n = readInt(), m = readInt(), k = readInt();
int x = readInt(), y = readInt();
boolean flag = false;
for (int i = 1; i <= k; i++) {
int a = readInt(), b = readInt();
if ((x + y) % 2 == (a + b) % 2) {
flag = true;// 存在奇偶性相同的朋友
}
}
printWriter.println(flag ? "No" : "Yes");
}
B题
河上有一条由 n n n 块带着颜色的木板构成的一座桥,维卡想要通过这座桥,但是他只能踩着相同颜色的木板扩过,否则会破坏桥的外观,现在,维卡站在第一块木板前的地面上。然后,维卡每走 m + 1 m+1 m+1 步,就要跨过 i 1 − 1 , i 2 − i 1 − 1 , i 3 − i 2 − 1 , … , i m − i m − 1 − 1 , n − i m i_1−1,i_2−i_1−1,i_3−i_2−1,…,i_m−i_m−1−1,n−i_m i1−1,i2−i1−1,i3−i2−1,…,im−im−1−1,n−im块木板。当然维卡现在有一种颜料可以改变其中跨越的木板的颜色,求出所有颜色的木板最大的跨越距离中的最小值。
思路
- 先记录下每种颜色的木板的坐标
- 再记录下每种颜色木板要跨越的距离
- 对每种颜色木板的距离进行排序,再对改颜色跨越最大距离进行减半(因为可以改变中间跨越的一块木板颜色,修改最中间的那块木板收益最大)
- B题思路来源
代码
public static void solve() throws IOException {
int n = readInt(), k = readInt();
//用于记录每种颜色的木板出现的坐标
List<Integer>[] list = new List[k + 5];
Arrays.setAll(list, g -> new ArrayList<>());
for (int i = 1; i <= k; i++) {
list[i].add(0);//头
}
for (int i = 1; i <= n; i++) {
int a = readInt();
list[a].add(i);
}
for (int i = 1; i <= k; i++) {
list[i].add(n + 1);//尾
}
//用于记录相同颜色木板要跨越的距离
List<Integer>[] dis = new List[k + 5];
Arrays.setAll(dis, g -> new ArrayList<>());
for (int i = 1; i <= k; i++) {
for (int j = 1; j < list[i].size(); j++) {
dis[i].add(list[i].get(j) - list[i].get(j - 1) - 1);//按照题目进行模拟
}
}
int res = Integer.MAX_VALUE;
for (int i = 1; i <= k; i++) {
Collections.sort(dis[i]);
int t = dis[i].get(dis[i].size() - 1);
dis[i].remove((Integer) t);
dis[i].add(t / 2);// 使用颜料让最大值缩小
Collections.sort(dis[i]);
res = Math.min(res, dis[i].get(dis[i].size() - 1));//在所有颜色模板的最大距离中取最小值
}
printWriter.println(res);
}
C题
有两个长度为 n n n 的非负整数数组,在一次操作中,维卡根据以下原则构造了一个新数组 c : c i = ∣ a i − b i ∣ c:c_i=|a_i−b_i| c:ci=∣ai−bi∣。然后将数组 c重命名为数组 b,同时将数组 b重命名为数组 a,之后维卡对它们重复上述操作。如果数组 a中的所有元素在经过一定次数的此类操作后都变为零,输出“Yes”,否则输出“No”
思路
- 先不考虑全局的情况,而是拆分成每一个小对,每一对就是 ( a i , b i ) (a_i, b_i) (ai,bi),每次都考虑每队 a i − b i a_i - b_i ai−bi的差值为0时首次出现的时间,因为后面再出现0就是周期性(周期为3)出现的,最终只要每一对的差值为0时首次出现的时间相同,那么最终 a数组所有元素就可以变为0
- 然而想要知道他们差值为0时首次出现的时间,这取决于
a
i
a_i
ai和
b
i
b_i
bi的奇偶性
图片源于灵神- a i a_i ai和 b i b_i bi的奇偶性是上面三种情况之一,他们的差值首次为0出现的时间是固定的
- 如果 a i a_i ai和 b i b_i bi都为偶数时,分别除以一下他们的最大公约数后等价于回到上面的三种情况之
- 思路源于灵神
public static void solve() throws IOException {
int n = readInt();
int[] a = new int[n + 1], b = new int[n + 1];
for (int i = 1; i <= n; i++) a[i] = readInt();
for (int i = 1; i <= n; i++) b[i] = readInt();
int cur = 0;
for (int i = 1; i <= n; i++) {
int t = check(a[i], b[i]);
if (cur == 0) {
cur = t;
} else {
if (t != cur && t != 0) {
printWriter.println("No");
return;
}
}
}
printWriter.println("Yes");
}
// 判断ai和bi的奇偶性,间接判断0首次出现的时间
public static int check(int a, int b) {
if (a == 0 && b == 0) return 0;//都为 0不用考虑,因为任何时刻差值都为 0
int gcd = gcd(a, b);
a /= gcd; b /= gcd;
if ((a & 1) == 0) return 1;// a为偶数,b为奇数
if ((b & 1) == 0) return 2;// a为奇数,b为偶数
return 3;// a为奇数,b为奇数
}
// 求出最大公约数
public static int gcd(int a, int b) {
return b == 0 ? a : gcd(b, a % b);
}