A. World Cup
题意:世界杯32支队伍.给定它们的能力值,求中国队能得到的最高排名.
比赛规则:32进16,4人一组,组内前两名晋级.16进8就是每组的第二名和旁边一组的第一名对战,第一名和旁边一组第二名对战.后面就是规定的1V1晋级单挑了
能力值其实就决定了最后的排名,最好的情况其实就是每次对战的时候碰到的都是没有被淘汰的垫底选手,当然自己垫底就结束,一顿模拟就行了
import java.io.*;
import java.util.*;
public class Main {
private static void solve() {
int []a=new int[33];
int dev=0;
for(int i=0;i<32;i++){
a[i]=sc.nextInt();
if(a[i]<=a[0])
dev++;
}
if(dev==32)
out.println(1);
else if (dev>=28)
out.println(2);
else if(dev>=14)
out.println(4);
else if(dev>=7)
out.println(8);
else if(dev>=3)
out.println(16);
else out.println(32);
}
public static void main(String[] args) {
int T = sc.nextInt();
while (T-- > 0) {
solve();
}
out.flush();
out.close();
}
static Kattio sc = new Kattio();
static PrintWriter out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(System.out)));
static class Kattio {
static BufferedReader r;
static StringTokenizer st;
public Kattio() {
r = new BufferedReader(new InputStreamReader(System.in));
}
public String next() {
try {
while (st == null || !st.hasMoreTokens()) {
st = new StringTokenizer(r.readLine());
}
return st.nextToken();
} catch (Exception e) {
return null;
}
}
public int nextInt() {
char[] str = next().toCharArray();
int i = 0;
boolean neg = false;
if (str[0] == '-') {
i = 1;
neg = true;
}
int ans = 0;
for (; i < str.length; i++) ans = ans * 10 + (str[i] - '0');
return neg ? -ans : ans;
}
public long nextLong() {
char[] str = next().toCharArray();
int i = 0;
boolean neg = false;
if (str[0] == '-') {
i = 1;
neg = true;
}
long ans = 0;
for (; i < str.length; i++) ans = ans * 10 + (str[i] - '0');
return neg ? -ans : ans;
}
public double nextDouble() {
return Double.parseDouble(next());
}
}
}
C. Permutation Counting 4
题意: 给定n对[l,r]代表第i个数的范围,求n的全排列中满足该条件的序列数mod2
这题原理有关线性代数的满秩矩阵性质,博主线代没怎么学就只能画画了,最后发现建立l-1,r的边,最后能建立一颗有n+1的树就是奇数个序列其实画出来的时候就能明白一大半但是很难证明.
import java.util.*;
public class Main {
// 主方法
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int t = scanner.nextInt(); // 读取测试案例数量
while (t-- > 0) {
solve(scanner); // 解决每个案例
}
scanner.close();
}
// 解决每个案例的函数
private static void solve(Scanner scanner) {
int n = scanner.nextInt(); // 读取节点数量
long sum = 0; // 记录访问的节点数量
List<List<Integer>> a = new ArrayList<>(n + 1); // 图的邻接表
for (int i = 0; i <= n; i++) {
a.add(new ArrayList<>()); // 初始化邻接表
}
boolean[] vis = new boolean[n + 1]; // 访问标记数组
// 读取边并构建图
for (int i = 1; i <= n; i++) {
int u = scanner.nextInt() - 1; // 节点编号从1开始,转换为0
int v = scanner.nextInt() ;
a.get(u).add(v); // 添加边
a.get(v).add(u); // 无向图
}
// 深度优先搜索函数
sum = dfs(0, a, vis, sum); // 从节点0开始DFS
// 检查是否所有节点都被访问
if (sum == n + 1) {
System.out.println(1); // 所有节点都连通
} else {
System.out.println(0); // 存在未连通的节点
}
}
// 深度优先搜索实现
private static long dfs(int u, List<List<Integer>> a, boolean[] vis, long sum) {
if (!vis[u]) { // 如果未访问
sum++; // 访问节点计数加1
vis[u] = true; // 标记为已访问
}
for (int p : a.get(u)) { // 遍历邻接节点
if (!vis[p]) {
sum = dfs(p, a, vis, sum); // 递归访问
}
}
return sum; // 返回已访问节点数量
}
}
G. The Median of the Median of the Median
题意略,大意就是找中位数的中位数的中位数
经典套路:二分中位数
check中求<=mid的数量
若<=mid的数量 >= 中位数的定义数量,则中位数一定<=mid,r=mid
反之,中位数一定>mid,l=mid
最后答案即为r
那么思考一下check中如何求<=mid的数量?先通过A数组求B数组,再通过B数组求C数组中<=mid的数量
用前缀和去统计个数
import java.util.Arrays;
import java.util.Scanner;
public class Main {
static final int MAXN = 2010; // 最大数组长度
static int n, tot; // n 为输入的数组长度,tot 为子数组的总数
static int[] a = new int[MAXN]; // 输入数组
static int[] p = new int[MAXN]; // 排序后的数组
static int[] prea = new int[MAXN]; // 前缀和数组,用于存储小于等于某个值的计数
static int[][] b = new int[MAXN][MAXN]; // b[i][j] 表示区间 [i, j] 中位数是否 <= val
static int[][] preb = new int[MAXN][MAXN]; // 统计 b 的前缀和
// 判断中位数是否 <= val
static boolean check(int val) {
for (int i = 1; i <= n; ++i) {
prea[i] = prea[i - 1] + (a[i] <= val ? 1 : 0); // 计算前缀和
}
for (int i = 1; i <= n; ++i) {
for (int j = i; j <= n; ++j) {
int sz = (j - i + 1);
// 判断区间 [i, j] 的中位数是否 <= val
b[i][j] = (prea[j] - prea[i - 1] >= (sz + 1) / 2) ? 1 : 0;
}
}
for (int i = 1; i <= n; ++i) {
for (int j = 1; j <= n; ++j) {
// 计算从左上角 (1, 1) 到右下角 (i, j) 的 b 的总和
preb[i][j] = b[i][j] + preb[i - 1][j] + preb[i][j - 1] - preb[i - 1][j - 1];
}
}
int res = 0; // 统计中位数 <= val 的个数
for (int i = 1; i <= n; ++i) {
for (int j = i; j <= n; ++j) {
// 统计所有子区间 [l, r] 中 b 取值为 1 的个数
int num = preb[j][j] - preb[i - 1][j] - preb[j][i - 1] + preb[i - 1][i - 1];
int len = j - i + 1;
int sz = (1 + len) * len / 2; // 子区间总个数
if (num >= (sz + 1) / 2) {
++res; // 中位数符合条件,计数加一
}
}
}
// 如果数量超过一半,说明中位数 <= val
return res >= (tot + 1) / 2;
}
public static void solve() {
Scanner scanner = new Scanner(System.in);
n = scanner.nextInt(); // 输入数组长度
for (int i = 1; i <= n; ++i) {
a[i] = scanner.nextInt(); // 输入数组元素
p[i] = a[i]; // 复制到排序数组
}
Arrays.sort(p, 1, n + 1); // 排序数组
tot = (1 + n) * n / 2; // 计算子数组总数
int l = 1, r = n; // 二分查找的左右边界
int m, id = 1; // m 为中间值,id 用于记录符合条件的下标
while (l <= r) {
m = (l + r) / 2; // 计算中间值
if (check(p[m])) { // 检查中位数是否符合条件
id = m; // 更新符合条件的下标
r = m - 1; // 继续向左查找
} else {
l = m + 1; // 向右查找
}
}
System.out.println(p[id]); // 输出最终结果
}
public static void main(String[] args) {
solve(); // 调用求解函数
}
}
M. Find the Easiest Problem
题意: 输出最多队伍通过的题号
输入: 队名,题号,判定结果
很简单的模拟,用hashset去重来统计,最后比较各个set的长度(通过队伍数)
import java.util.*;
public class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int t = scanner.nextInt();
while (t-- > 0) {
int n = scanner.nextInt();
Set<String>[] st = new HashSet[26];
for (int i = 0; i < 26; i++) {
st[i] = new HashSet<>();
}
for (int i = 0; i < n; i++) {
String team = scanner.next();
char prob = scanner.next().charAt(0);
String status = scanner.next();
if (status.equals("accepted")) {
st[prob - 'A'].add(team);
}
}
int idx = 0;
for (int i = 1; i < 26; i++) {
if (st[idx].size() < st[i].size()) {
idx = i;
}
}
System.out.println((char) ('A' + idx));
}
scanner.close();
}
}
我写这题很奇怪,我用map<string,set>map去存的话会WA,题目中好像是通过队伍数一样,按照题号来排序,越小越好,但是map遍历的时候用entryset遍历,输出的顺序是按照key的hash值来决定的.所以可能不太行