B 空调遥控
差分 + 前缀和
-p <= a[i] - K <= p
-p - a[i] <= -K <= p - a[i]
a[i] - p <= K <= a[i] + p
p 和 a[i] 是给定的,我们就用差分,求出每个人适宜的温度区间,覆盖表示
0 1 2 3 4 5 6 7 8 K
2 1 1 1 0 -1 0 -1 cnt
K是温度,cnt是差分数组,前缀和表示当温度为 K 时,有多少个 a[i] 的舒适区间覆盖此温度
/**
* @author :Changersh
* @date : 2023/3/31 19:29
*/
import java.io.*;
import java.util.*;
import java.lang.*;
public class Main {
private static int T, N = (int) 2e6 + 10, n, p, l, r, x, mx, ans;
private static int[] s = new int[N];
private static String[] sp;
public static void main(String[] args) throws IOException {
BufferedReader sc = new BufferedReader(new InputStreamReader(System.in));
sp = sc.readLine().split(" ");
n = Integer.parseInt(sp[0]);
p = Integer.parseInt(sp[1]);
sp = sc.readLine().split(" ");
for (int i = 0; i < n; i++) {
x = Integer.parseInt(sp[i]);
l = Math.max(0, x - p);
r = x + p;
s[l] += 1;
s[r + 1] -= 1;
}
for (int i = 1; i <= n; i++) {
s[i] += s[i - 1];
if (s[i] > mx) {
mx = s[i];
}
}
System.out.println(mx);
}
}
C 来点gcd
同样的写法,Java超时了,没办法。
给出一个 x,问是否存在非空子集的 gcd是x,则这些数一定是 x 的倍数,且包含的越多,gcd是x的概率就越大。
我们标记上,输入过的数字。
我们是对每组样例都进行预处理,如果该数出现过,就遍历它的所有倍数,如果该倍数也出现过,求gcd。
最后遍历询问,判断每个输入的 x 的gcdx是否是x即可
注意:对于每组样例,都要初始化,否则会乱掉的
数学
#include <iostream>
#include <cstring>
using namespace std;
const int maxn = 2e6 + 10;
int gcd(int x, int y) {
return y ? gcd(y, x % y) : x;
}
int T, n, m;
int a[maxn];
int gcdx[maxn];
int main() {
scanf("%d", &T);
while (T--) {
scanf("%d%d", &n, &m);
memset(gcdx, 0, sizeof(int) * (n + 1));
memset(a, 0, sizeof(int) * (n + 1));
for (int i = 1; i <= n; i++) {
int x;
scanf("%d", &x);
a[x] = 1;
}
for (int i = 1; i <= n; i++) {
for (int j = i; j <= n; j += i) {
if (a[j]) gcdx[i] = gcd(gcdx[i], j);
}
}
while (m--) {
int x;
scanf("%d", &x);
gcdx[x] == x ? printf("YES\n") : printf("NO\n");
}
}
return 0;
}
D 优美字符串
模拟
从第一个元素开始,统计和它后面一个元素相等的个数
/**
* @author :Changersh
* @date : 2023/3/31 18:59
*/
import java.io.*;
import java.util.*;
import java.lang.*;
public class Main {
private static int N = 2010, n, k, x, y, T;
private static char[] a;
public static void main(String[] args) throws IOException {
BufferedReader sc = new BufferedReader(new InputStreamReader(System.in));
T = Integer.parseInt(sc.readLine());
while (T-- > 0) {
int ans = 0;
a = sc.readLine().toCharArray();
n = a.length;
for (int i = 0; i < n - 1; i++) if (a[i] == a[i + 1]) ans++;
System.out.println(ans + n);
}
}
}
E 数字游戏
数学
公式可以推出来
偶数个 1
最低位是 0:1010–10–11–1–0 2*cnt
最低位是 1:1001–1–0 cnt 这个算错了
1111–111–110–10–11–1–0 …------- (cnt - 1) * 2
10111–111–110–10–11–1–0 (以防万一多算几个,发现是 第二个)
奇数个 1
最低位是 0:1110–1111–111–110–10–11–1–0 2 * cnt + 1
最低位是 1:1101–1100–100–101–1–0 2 * cnt - 1
/**
* @author :Changersh
* @date : 2023/4/4 8:56
*/
import java.io.*;
import java.util.*;
import java.lang.*;
public class Main {
private static int T, N = (int) 1e6 + 10, x, cnt = 0, ans;
private static boolean ck;
public static void main(String[] args) throws IOException {
T = sc.nextInt();
while (T-- > 0) {
cnt = 0;
x = sc.nextInt();
if ((x & 1) == 1) ck = true;
else ck = false;
while (x > 0) {
if ((x & 1) == 1) cnt++;
x >>= 1;
}
if ((cnt & 1) == 1) { // 奇数个 1
if (ck) ans = 2 * cnt - 1;
else ans = 2 * cnt + 1;
} else {
if (ck) ans = (cnt - 1) * 2;
else ans = 2 * cnt;
}
pw.println(ans);
}
pw.close();
}
}
F 体操队形
dfs
方案数只有 10 个,暴力搜索
dfs的判断条件:该点是否访问过? & 该点的后面一个点是否访问过?
/**
* @author :Changersh
* @date : 2023/3/31 20:22
*/
import java.io.*;
import java.util.*;
import java.lang.*;
public class Main {
private static int N = 20, T, n, ans;
private static int[] a = new int[N];
private static boolean[] vis = new boolean[N];
private static String[] sp;
public static void main(String[] args) throws IOException {
BufferedReader sc = new BufferedReader(new InputStreamReader(System.in));
BufferedWriter pw = new BufferedWriter(new OutputStreamWriter(System.out));
n = Integer.parseInt(sc.readLine());
sp = sc.readLine().split(" ");
for (int i = 0; i < n; i++) a[i + 1] = Integer.parseInt(sp[i]);
dfs(0);
pw.write(("" + ans));
pw.close();
}
private static void dfs(int u) {
if (u >= n) {
ans++;
return;
}
for (int i = 1; i <= n; i++) {
if (vis[i]) continue;
if (vis[a[i]]) break; // 本该在 i 后面的选过了
vis[i] = true;
dfs(u + 1);
vis[i] = false;
}
}
}
H 分组
二分答案
二分人数最多的组的人数
/**
* @author :Changersh
* @date : 2023/4/4 8:56
*/
import java.io.*;
import java.util.*;
import java.lang.*;
public class Main {
private static int N = (int) 1e5 + 10, m, n, ans = 0x3f3f3f3f, l = 1, r, x, mx;
private static int[] a = new int[N];
public static void main(String[] args) throws IOException {
n = sc.nextInt();
m = sc.nextInt();
for (int i = 0; i < n; i++) {
x = sc.nextInt();
a[x]++;
mx = Math.max(mx, a[x]);
}
r = mx + 1;
while (l < r) {
int mid = l + ((r - l) >> 1);
if (check(mid)) {
r = mid;
ans = Math.min(ans, mid);
} else l = mid + 1;
}
if (check(ans)) pw.println(("" + ans));
else pw.println("-1");
pw.close();
}
private static boolean check(int x) {
int cnt = 0;
for (int i = 1; i <= n; i++) {
cnt += a[i] / x + (a[i] % x == 0 ? 0 : 1);
}
return cnt <= m;
}
}
I 跳跳跳
区间dp,记忆化搜索
发现在任意时刻,已经跳的区间都是一个联通块(区间)
考虑把数组复制一份,贴在原数组后面,构成环形数组
f[l][r]=max(f[l+1][r]+a[l]∗len,f[l][r−1]+a[r]∗len)
len
是区间长度
/**
* @author :Changersh
* @date : 2023/4/4 8:56
*/
import java.io.*;
import java.util.*;
import java.lang.*;
public class Main {
private static int T, N = 4010, n;
private static long ans = 0;
private static int[] a = new int[N];
private static long[][] f = new long[N][N];
public static void main(String[] args) throws IOException {
n = sc.nextInt();
for (int i = 1; i <= n; i++) {
a[i] = sc.nextInt();
a[i + n] = a[i]; // 复制一份,构造环形数组
}
// 对每个点都进行一次dfs,找到最大值
for (int i = 1; i <= n; i++) ans = Math.max(ans, dfs(i, i + n - 1));
pw.println(ans);
pw.close();
}
private static long dfs(int l, int r) {
if (l == r) return a[l];
if (f[l][r] != 0) return f[l][r];
long len = r - l + 1;
return f[l][r] = Math.max(dfs(l + 1, r) + len * a[l], dfs(l, r - 1) + len * a[r]);
}
}