CodeForces Round883 div3
A题
题意:把所有小于地面长度的绳子给剪断;
private static void problemA(Fast fast) {
int t = fast.nextInt();
while(--t >= 0) {
int n = fast.nextInt();
int cnt = 0;
for (int i = 0; i < n; i++) {
int a = fast.nextInt();
int b = fast.nextInt();
if (a > b) {
cnt++;
}
}
fast.write(cnt + "\n");
fast.flush();
}
}
B题
题意:3x3的二维矩阵,找出特例的,横竖斜三个元素相等的字符,排除字符’.';
private static void problemB(Fast fast) {
int t = fast.nextInt();
go:while(--t >= 0) {
var chars = new char[3][3];
chars[0] = fast.nextLine().toCharArray();
chars[1] = fast.nextLine().toCharArray();
chars[2] = fast.nextLine().toCharArray();
for (int i = 0; i <= 2; i++) {
if (chars[i][0] == chars[i][1] && chars[i][1] == chars[i][2] && chars[i][0] != '.') {
fast.write(chars[i][0] + "\n");
fast.flush();
continue go;
}
}
for (int i = 0; i <= 2; i++) {
if (chars[0][i] == chars[1][i] && chars[1][i] == chars[2][i] && chars[0][i] != '.') {
fast.write(chars[0][i] + "\n");
fast.flush();
continue go;
}
}
if (chars[0][0] == chars[1][1] && chars[1][1] == chars[2][2] && chars[2][2] != '.') {
fast.write(chars[0][0] + "\n");
fast.flush();
continue ;
}
if (chars[0][2] == chars[1][1] && chars[1][1] == chars[2][0] && chars[2][0] != '.') {
fast.write(chars[0][2] + "\n");
fast.flush();
continue ;
}
fast.write("DRAW" + "\n");
fast.flush();
}
}
C题
题意:给N个数组,数组长度为m,和一个时间time。每个数组代表一个人,数组的每个元素代表这个人做完第i个题目的时间为a[i],问最优解。
本质就是时间排序之后再依次做完。
private static void problemC(Fast fast) {
int t = fast.nextInt();
go:while(--t >= 0) {
int n = fast.nextInt();
int m = fast.nextInt();
int h = fast.nextInt();
var ans = new long[n][2];
var nums = new long[n][m];
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
nums[i][j] = fast.nextInt();
}
Arrays.sort(nums[i]);
int time = 0;
for (int k = 0; k < nums[i].length; k++) {
if (nums[i][k] + time <= h) {
time += nums[i][k];
ans[i][1] += time;
ans[i][0]++;
} else {
break;
}
}
}
Arrays.sort(ans, 1, ans.length, (a, b) -> {
if (a[0] == b[0]) {
return Long.compare(a[1], b[1]);
} else {
return Long.compare(b[0], a[0]);
}
});
for (int i = 1; i < n; i++) {
if (ans[0][0] > ans[i][0]) {
fast.write(i + "\n");
fast.flush();
continue go;
} else if (ans[0][0] == ans[i][0] && ans[0][1] <= ans[i][1]) {
fast.write(i + "\n");
fast.flush();
continue go;
}
}
fast.write(n + "\n");
fast.flush();
}
}
D题
题意:给定N个相等的等腰三角形,三角形在同一条垂直线上,可能会有重叠部分,求面积和。
private static void problemD(Fast fast) {
int t = fast.nextInt();
while(--t >= 0) {
double n = fast.nextInt();
double d = fast.nextInt();
double h = fast.nextInt();
double pre = 0;
double ans = 0.0;
for (int i = 0; i < n; i++) {
int y = fast.nextInt();
if (y >= pre) {
ans += (d * h) / 2.0;
} else {
double p = pre - y;
ans += ((d * h) - (p * d * p / h)) / 2.0;
}
pre = y + h;
}
fast.write(ans + "\n");
fast.flush();
}
}
E题
题意:有一个1 + k + k2 + … + kn - 1的等比数列q,给一个数字num,问num是否可以被表示为q,q的长度大于等于3,k>1;
因为num的值最大为1018,所以等比数列的长度小于等于60。可以枚举长度,再对k的值做二分查询。由等比数列的特性可知,当长度为n时,k < Math.pow(num, 1.0 / n - 1);则二分的左边界为2,右边界为Math.pow(num, 1.0 / n - 1);二分查询成功匹配则直接输出yes;
private static void problemE(Fast fast) {
int t = fast.nextInt();
go:while(--t >= 0) {
long x = fast.nextLong();
int maxBit = 60;
for (int i = 3; i <= maxBit; i++) {
long l = 2;
long r = (long)Math.pow(x, 1.0 / (i - 1));
while(l <= r) {
long mid = (l + r) >> 1;
long ans = 1;
long sum = 0;
for (int j = 1; j <= i; j++) {
sum += ans;
ans *= mid;
}
if (sum < x) {
l = mid + 1;
} else if (sum > x) {
r = mid - 1;
} else {
fast.write("YES\n");
continue go;
}
}
}
fast.write("NO\n");
}
fast.flush();
}
F题
题意:数组中有一个模仿怪,每两回合之内必定会模仿成其他元素,要求5次操作的情况下成功找出模仿怪。
由于模仿怪两回合之内必定变幻元素,所以可前两次不进行操作,这两次操作期间如果出现一个元素index大于之前map存的元素的次数,进行删除操作,删除不等于index的元素;现在数组的每一个元素都是一样的,只需要等待模仿怪再一次变幻元素即可轻松找出。
private static void problemF(Fast fast) {
int t = fast.nextInt();
go: while(--t >= 0) {
HashMap<Integer, Integer> map = new HashMap<>();
int n = fast.nextInt();
for (int i = 0; i < n; i++) {
map.merge(fast.nextInt(), 1, Integer::sum);
}
int index = -1;
Map<Integer, Integer> newMap;
var arr = new int[n + 1];
while (index == -1) {
newMap = new HashMap<>();
fast.write("- 0\n");
fast.flush();
for (int i = 1; i <= n; i++) {
arr[i] = fast.nextInt();
if (!map.containsKey(arr[i])) {
index = i;
}
newMap.merge(arr[i], 1, Integer::sum);
}
if (index != -1) {
fast.write("! " + index + "\n");
fast.flush();
continue go;
}
for (var key : newMap.keySet()) {
if (map.get(key) == newMap.get(key) - 1) {
index = key;
break;
}
}
}
// 删除操作
fast.write("- ");
var list = new ArrayList<Integer>();
for (int i = 1; i <= n; i++) {
if (arr[i] != index) {
list.add(i);
}
}
n -= list.size();
fast.write(list.size() + " ");
list.forEach(ele -> fast.write(ele + " "));
fast.write("\n");
fast.flush();
int p = 2;
while(--p >= 0) {
int num = -1;
for (int i = 1; i <= n; i++) {
if (fast.nextInt() != index) {
num = i;
}
}
if (num != -1) {
fast.write("! " + num + "\n");
fast.flush();
continue go;
}
fast.write("- 0\n");
fast.flush();
}
}
}
G题
题意:题意很好理解,就是一个状态消耗d天能转移到另外一个状态。给出一个初始状态,询问到状态0最少需要消耗多少天。
第一个问题是我们需要写出状态转移式子。
题意说u状态吃药之后能转移到v状态,当我们初始状态为s时,我们吃药得到的状态应该为s | v & ~u。
首先是s|v,是将治愈之后的v中的1以及原先s状态的1都置为1,0保持不变。得到这个状态后 & ~u,是将u中的所有1全部置为0,其余位置保持不变。
将0~1023视为u的集合,我们可以找出每一个u在m种药的情况下,所对应的v集合。也就是通俗易懂的u到v的路径长度为d。
long start = fast.nextLong();
int p = converterToInt(start, n);
var map = new HashMap<Integer, Map<Integer, Integer>>();
for (int i = 0; i < m; i++) {
int d = fast.nextInt();
int u = converterToInt(fast.nextLong(), n);
int v = converterToInt(fast.nextLong(), n);
map.computeIfAbsent(u, value -> new HashMap<>()).merge(v, d, Math::min);
}
var mapG = new HashMap<Integer, Map<Integer, Integer>>();
for (int i = 0; i < 1024; i++) {
var cur = new HashMap<Integer, Integer>();
for (var u : map.keySet()) {
for (var v : map.get(u).keySet()) {
int target = (i | v) & (~u);
cur.merge(target, map.get(u).get(v), Math::min);
}
}
mapG.put(i, cur);
}
mapG保存了两点之间的连通路径,那么题目转换成了给出N条路径,询问从s到0的最短路径为多少。
此时直接上dijkstra最短路径模板即可。
完整代码如下:
private static void problemG(Fast fast) {
int t = fast.nextInt();
while(--t >= 0) {
int n = fast.nextInt();
int m = fast.nextInt();
long start = fast.nextLong();
int p = converterToInt(start, n);
var map = new HashMap<Integer, Map<Integer, Integer>>();
for (int i = 0; i < m; i++) {
int d = fast.nextInt();
int u = converterToInt(fast.nextLong(), n);
int v = converterToInt(fast.nextLong(), n);
map.computeIfAbsent(u, value -> new HashMap<>()).merge(v, d, Math::min);
}
var mapG = new HashMap<Integer, Map<Integer, Integer>>();
for (int i = 0; i < 1024; i++) {
var cur = new HashMap<Integer, Integer>();
for (var u : map.keySet()) {
for (var v : map.get(u).keySet()) {
int target = (i | v) & (~u);
cur.merge(target, map.get(u).get(v), Math::min);
}
}
mapG.put(i, cur);
}
dijkstra dijkstra = new dijkstra(p, mapG, 1024);
int ans = dijkstra.dis[0] == Integer.MAX_VALUE ? -1 : dijkstra.dis[0];
fast.write(ans + "\n");
}
fast.flush();
}
private static int converterToInt(long num, int len) {
int sum = 0;
for (int i = len - 1; i >= 0; i--) {
long t = num / (long)Math.pow(10, i);
if ((t & 1) == 1) {
sum += (1 << i);
}
}
return sum;
}
public static class dijkstra {
public int[] dis;
public Map<Integer, Map<Integer, Integer>> map;
public boolean[] vis;
public PriorityQueue<Node> queue;
static class Node {
int dis;
int id;
public Node(int dis, int id) {
this.dis = dis;
this.id = id;
}
}
public dijkstra(int start, Map<Integer, Map<Integer, Integer>> map, int len) {
dis = new int[len + 5];
Arrays.fill(dis, Integer.MAX_VALUE);
this.map = map;
vis = new boolean[len + 5];
dis[start] = 0;
queue = new PriorityQueue<>(len + 5, Comparator.comparingInt(a -> a.dis));
queue.add(new Node(0, start));
while(!queue.isEmpty()) {
Node node = queue.poll();
int id = node.id;
if (vis[id]) {
continue;
}
vis[id] = true;
Map<Integer, Integer> next = map.get(id);
if (next != null) {
for (var ints : next.keySet()) {
int w = next.get(ints);
if (dis[ints] > dis[id] + w) {
dis[ints] = dis[id] + w;
queue.add(new Node(dis[ints], ints));
}
}
}
}
}
}