牛客周赛 Round 87
写在前面
比赛地址:https://ac.nowcoder.com/acm/contest/105623。
因为各种原因,赛时全程用的 java,妈的虽然都会但是手太生了进而影响脑子速度直接倒闭,周赛都 AK 不了了呃呃。
以下记一点 java 的算法题语法。
A
水题,直接做。
java Scanner:
- Java Scanner类的常用方法及用法(很详细)_scanner在java中的用法-CSDN博客;
- Java Scanner类详解_java中scanner三个步骤-CSDN博客
- Java 从零开始(27)Java Scanner 类 - 知乎
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
Integer[] a = new Integer[3];
for (int i = 0; i < 3; i++) {
a[i] = sc.nextInt();
}
System.out.println((a[0] > a[1] && a[1] < a[2]) ? "YES" : "NO");
}
}
B
水题,直接枚举分界即可。
java Scanner:
- 换行符问题:在使用 next() 和 nextLine() 方法时,需要注意换行符的处理。next() 方法不会消耗换行符,而 nextLine() 会读取直到换行符。因此,在连续调用这两个方法时,可能需要调用一个额外的 nextLine() 来消耗掉 next() 留下的换行符。
String.substring(beginIndex, endIndex):
- 得到子串
String[beginIndex, endIndex - 1]; beginIndex为起始下标;endIndex为结束下标(不包含endIndex)。
Integer.parseInt(@NotNull String s, int radix):
s转换为int;radix为解析s的进制;
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = Integer.parseInt(sc.nextLine());
/*
int n = sc.nextInt();
sc.nextLine();
*/
for (int t = 0; t < n; t++) {
String a = sc.nextLine();
int len = a.length();
int ans = 0;
for (int i = 0; i < len - 1; i++) {
int x = Integer.parseInt(a.substring(0, i + 1), 10);
int y = Integer.parseInt(a.substring(i + 1, len), 10);
ans = Math.max(ans, x + y);
}
System.out.println(ans);
}
}
}
C
妈的爆 int 了查了一万年没查出来,唐。
发现仅关注每个数的符号,考虑把正数负数分别视为 -1/1,则显然有如下策略最优:
- 若
s[i] == '<',仅需保证 \(a_i\) 为负数; - 若
s[i] == '>',仅需保证 \(a_i\) 为正数; - 若
s[i] == 'Z',仅需保证 \(a_i, a_{i - 1}\) 同号,若需要修改仅需令 \(a_i:=a_{i - 1}\) 即可。
直接模拟即可。
注意原数列中两个数直接相乘可能爆 int 呃呃。
import java.util.Scanner;
public class Main {
public static Scanner sc;
public static long[] arr;
public static String s;
public static void solve() {
int n = sc.nextInt();
sc.nextLine();
arr = new long[n];
for (int i = 0; i < n; i++) {
arr[i] = sc.nextLong();
}
sc.nextLine();
s = sc.nextLine();
int ans = 0;
for (int i = 0; i < n; i++) {
if (s.charAt(i) == '<') {
if (arr[i] >= 0) {
++ ans;
}
arr[i] = -1;
} else if (s.charAt(i) == '>') {
if (arr[i] <= 0) {
++ ans;
}
arr[i] = 1;
} else if (s.charAt(i) == 'Z') {
if (arr[i] * arr[i - 1] <= 0) {
++ ans;
}
arr[i] = arr[i - 1];
}
}
System.out.println(ans);
}
public static void main(String[] args) {
sc = new Scanner(System.in);
int T = sc.nextInt();
sc.nextLine();
for (int t = 0; t < T; t++) {
solve();
}
}
}
/*
1
10
1 1 1 -1 -1 -1 1 1 1 1
>>>ZZZ>>>>
*/
D
先特判 \(n=1\),此时无法进行任何操作,而且答案可能为负数。若 \(n\ge 2\) 则答案一定非负。
发现用 2、3 可以凑出来大于 1 的所有数,进一步发现经过一通操作之后,被删除的数肯定是原数列中若干段长度不小于 2 的连续区间,操作的类型和先后顺序是没有影响的,于是直接钦定删除的顺序是从左到右依次删的。
考虑一个简单 DP,令 \(a_{n + 1} = 0\),记 \(f_{i}(1\le i\le n+1)\) 表示钦定考虑到前 \(i\) 个数,最后一个没有被删除的数为 \(f_{i}\) 时的最大价值之和。初始化 \(f_{0} = 0\),考虑保留 \(a_ii\) 为最后没有被删除的数时,上一个数的位置,则有显然的转移:
答案即为 \(f_{n + 1}\)。
观察转移方程可以发现第三种转移是没用的,已经被第二种包含了,套路地记前缀和 \(g_{i} = \max\limits_{0\le j\le i} f_{j}\) 优化上述转移即可,总时间复杂度 \(O(n)\) 级别。
import java.util.Scanner;
public class Main {
public static Scanner sc;
public static long[] arr;
public static long[] f;
public static long[] g;
public static void solve() {
int n = sc.nextInt();
sc.nextLine();
arr = new long[n + 2];
for (int i = 1; i <= n; i++) arr[i] = sc.nextInt();
arr[n + 1] = 0;
sc.nextLine();
if (n == 1) {
System.out.println(arr[1]);
return ;
}
f = new long[n + 2];
g = new long[n + 2];
for (int i = 0; i <= n; ++ i) f[i] = g[i] = 0;
for (int i = 1; i <= n + 1; i++) {
f[i] = f[i - 1] + arr[i];
if (i >= 3) f[i] = Math.max(f[i], g[i - 3] + arr[i]);
// if (i >= 4) f[i] = Math.max(f[i], g[i - 4] + arr[i]);
g[i] = Math.max(g[i - 1], f[i]);
}
System.out.println(f[n + 1]);
}
public static void main(String[] args) {
sc = new Scanner(System.in);
int T = sc.nextInt();
sc.nextLine();
for (int t = 0; t < T; t++) {
solve();
}
}
}
E
easy 构造。
先特判 \(n=1\) 时当且仅当 \(x=y\) 有解。
显然可以独立地构造每一个二进制位,考虑当前枚举到第 \(k\) 位。然后讨论 \(x, y\) 在这一位上的取值:
- \(x\) 为 0,\(y\) 为 0:\(a_1\sim a_n\) 这一位只能是 0;
- \(x\) 为 0,\(y\) 为 1:产生矛盾,无解;
- \(x\) 为 1,\(y\) 为 0/1:\(a_i\sim a_n\) 中这一位上至少有一个 1,且 1 的数量为偶数/奇数。
仅需要考虑 \(x\) 某位上为 1 时具体如何构造即可。
为了保证构造的数均为正数,显然应当使 \(a_1\sim a_n\) 该位上尽量都不为 0,于是考虑先将 \(a_1\sim a_n\) 该位上置 1,若 \(y\) 不满足条件则再选择一个数,将该数的该位再置 0 即可。
容易发现只要满足上述选择的某一位置 0 的数,只要不都是同一个数就保证可以满足构造的 \(a_1\sim a_n\) 均为正数。
import java.util.Scanner;
public class Main {
public static Scanner sc;
public static int[] arr;
public static void solve() {
int n = sc.nextInt();
int a = sc.nextInt();
int b = sc.nextInt();
sc.nextLine();
if (n == 1) {
System.out.println(a == b ? "YES\n" + a : "NO");
return ;
}
boolean flag = true;
arr = new int[n];
for (int i = 0; i < n; i++) arr[i] = 0;
for (int i = 0, p = 0; i < 31; i++) {
int x = (a >> i) & 1;
int y = (b >> i) & 1;
if (x == 0 && y == 0) continue;
if (x == 0 && y == 1) {
flag = false;
break;
}
for (int j = 0; j < n; ++ j) arr[j] += (1 << i);
if ((y == 1 && n % 2 == 0) || (y == 0 && n % 2 == 1)) {
arr[(++ p) % n] -= (1 << i);
}
}
for (int i = 0; i < n; ++ i) {
if (arr[i] == 0) {
flag = false;
break;
}
}
if (!flag) {
System.out.println("NO");
return ;
}
System.out.println("YES");
for (int i = 0; i < n; i++) {
System.out.print(arr[i] + " ");
}
System.out.println();
}
public static void main(String[] args) {
sc = new Scanner(System.in);
int T = sc.nextInt();
sc.nextLine();
for (int t = 0; t < T; t++) {
solve();
}
}
}
F
本场最搞笑题。
一眼看上去是某种考虑枚举每个位置是什么字符,然后记录每种前缀各出现了多少次的 DP,然而受 ? 的影响改变了字符串形态,无法 DP 计数记出现次数的最大值。
那咋办啊?先考虑怎么求合法的子序列 ovo 数量。发现 ovo 这个形状左右对称而且长度只有 3 非常优美,进一步容易发现,对于某个已确定的字符串 \(s\),记 \(\operatorname{pre}_i, \operatorname{suf}_i\) 分别表示前缀 \([1, i], [i, n]\) 中有多少 o,则合法子序列 ovo 数量为:
观察这个式子,容易发现 o 出现位置越接近两侧可能的贡献越大,v 出现位置越接近中间可能的贡献越大。于是贪心地猜测被修改成 v 的 ? 一定是字符串一段中间的一段连续区间,区间两侧的所有 ? 都被修改成 o。
发现 \(|s|\le 500\),考虑枚举被修改成 v 的 ? 的区间 \([l, r]\),然后大力构造修改后的字符串,按照上述式子对子序列计数并取最大值即可。
总时间复杂度 \(O(|s|^3)\) 级别。
import java.util.Scanner;
public class Main {
public static Scanner sc;
public static String s;
public static void solve() {
s = sc.nextLine();
int n = s.length();
s = "$" + s;
long ans = 0;
for (int l = 1; l <= n; ++ l) {
for (int r = l; r <= n; ++ r) {
long[] pre = new long[n + 2];
long[] suf = new long[n + 2];
long sum = 0;
for (int i = 0; i <= n + 1; ++ i) pre[i] = suf[i] = 0;
for (int i = 1; i <= n; ++ i) pre[i] = pre[i - 1] + ((s.charAt(i) == 'o' || ((i < l || i > r) && s.charAt(i) == '?')) ? 1 : 0);
for (int i = n; i >= 0; -- i) suf[i] = suf[i + 1] + ((s.charAt(i) == 'o' || ((i < l || i > r) && s.charAt(i) == '?')) ? 1 : 0);
for (int i = 1; i < l; ++ i) if (s.charAt(i) == 'v') sum += pre[i - 1] * suf[i + 1];
for (int i = l; i <= r; ++ i) if (s.charAt(i) == 'v' || s.charAt(i) == '?') sum += pre[i - 1] * suf[i + 1];
for (int i = r + 1; i <= n; ++ i) if (s.charAt(i) == 'v') sum += pre[i - 1] * suf[i + 1];
ans = Math.max(ans, sum);
}
}
System.out.println(ans);
}
public static void main(String[] args) {
sc = new Scanner(System.in);
int T = sc.nextInt();
sc.nextLine();
for (int t = 0; t < T; t++) {
solve();
}
}
}
写在最后
我是废物。

浙公网安备 33010602011771号