CodeForces Round883 div3 A~G

CodeForces Round883 div3

883_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));  
                    }  
                }  
            }  
        }  
    }  
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值