2022百度之星程序设计大赛 - 初赛 - 第一场

BD202201洞穴

题目

怪诞小镇的森林里有一个奇怪的洞穴群。
这个洞穴群包括 n 个洞穴(从 1 到 n 编号)。
每个洞穴中有若干岔路通向其他的洞穴,每条岔路都有一个长度。
日志3上记录了这个洞穴群的一个特点:任意两个洞穴间仅存在一条路径。
此外,日志的主人还在日志3上写下了任意两个洞穴间的距离。
现在小度想要知道这个洞穴群中,哪两个洞穴间存在岔路。

格式

输入格式

多组样例输入,第一行一个整数 T 表示数据组数。
每组数据包括 n+1 行。
第一行一个数字 n 表示洞穴的个数
之后 n 行每行 n 个整数,第 x 行的第 y 个整数 disx,y 表示洞穴 x 和洞穴 y 间的距离。

输出格式

对于每个样例,首先输出一行一个整数 k,表示洞穴群中存在 k 条岔路。
之后 k 行输出 k 个二元组 (x,y),表示洞穴 x 和洞穴 y 间有一条直接连通的岔路。
请按照 (x,y) 递增的顺序输出:即 x 不同时先输出 x 较小的 (x,y);x 相同时先输出 y 较小的 (x,y)。

数据范围

T≤10,1≤n≤100,disx,y≤105

样例

输入

1
5
0 1 2 3 3
1 0 1 2 2
2 1 0 3 3
3 2 3 0 4
3 2 3 4 0

输出

4
1 2
2 3
2 4
2 5

题解

任意两个洞穴间仅存在一条路径,说明答案是连通树。
没错,这题就是最小生成树kruskal-算法的板子。
结论:kruskal 算法不选择的边都能通过前面选的边组合形成。
kruskal 算法从权值最小的距离开始选,遇到第一条不选的边,如果这条边不是通过组合形成,则这两构成的边由于树的性质必然会破坏前面的一条边,导致要花费更多边去维护被破坏边的两个点的距离,而树的边数量是固定的。所以第一个不选的边能通过前面选的边组合形成。把第一个不选的边(组合形成)当成一条边,递推即可得到答案。

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.io.StreamTokenizer;
import java.util.*;

public class Main {
    static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
    static StreamTokenizer cin = new StreamTokenizer(br);

    static PrintWriter out = new PrintWriter(System.out);
    static int nextInt() throws Exception {
        cin.nextToken();
        return (int) cin.nval;
    }
    static String next() throws Exception {
        cin.nextToken();
        return cin.sval;
    }
    static double nextDouble() throws Exception {
        cin.nextToken();
        return cin.nval;
    }
    public static void main(String[] args) throws Exception {
        int cnt = nextInt();
        while (cnt --> 0) {
            int n = nextInt();
            int[][] g = new int[n * n - n][3];
            int[][] res = new int[n * n - n][];
            p = new int[n + 1];
            int idx = 0;
            for (int i = 1; i <= n; i++) {
                for (int j = 1; j <= i; j++) {
                    nextInt();
                }
                for (int j = i + 1; j <= n; j++) {
                    g[idx][0] = i;
                    g[idx][1] = j;
                    g[idx++][2] = nextInt();
                }
                p[i] = i;
            }
            Arrays.sort(g, 0, idx, (o1, o2) -> {
                return o1[2] - o2[2];
            });
            int ridx = 0;
            for (int i = 0; i < idx; i++) {
                int[] v = g[i];
                if (find(v[0]) != find(v[1])) {
                    p[find(v[0])] = find(v[1]);
                    res[ridx++] = v;
                }

            }
            Arrays.sort(res, 0, ridx, (o1, o2) -> {
                if (o1[0] == o2[0]) {
                    return o1[1] - o2[1];
                }
                return o1[0] - o2[0];
            });
            out.println(ridx);
            for (int i = 0; i < ridx; i++) {
                out.println(res[i][0] + " " + res[i][1]);
            }
            out.flush();
        }
    }
    static int[] p;
    static int find(int x) {
        if (p[x] != x) {
            p[x] = find(p[x]);
        }
        return p[x];
    }
}

BD202202小度养小猫

没有vip做不了这题,代码不保证一定正确。

题目

小度养了n只小猫,他每天按时给这n只小猫喂食,即第iii分钟他将给第i只小猫喂食。
但是他今天突然有事出去,前k分钟他不能给这些小猫喂食,而只能在第k+1 ~ k+n分钟喂食。
现在要求:今天的喂食顺序可以不同于之前的顺序,但是第iii只小猫今天吃食的时间不能早于自己之前吃食的时间i。
如果第i只小猫今天在第ti分钟吃食,那么它会产生(ti2−i2)∗ci的不满意度。
现在满足要求的前提下,请确定一个喂食顺序使不满意度之和最小并求出这个最小值(注:同一时间只能喂一只小猫)。

格式

输入格式

第一行两个正整数n,k表示小猫的数量和小度不能喂食的时间 。
第二行n个整数c1,c2,…,cn表示第i个小猫不满意度的系数。

输出格式

共一行输出一个数代表最小不满意度之和。

数据范围

1 ≤ k ≤ n ≤ 105, k ≤ 5*104,ci ≤ 104
答案保证在long long范围内。

样例

输入

5 2
4 2 1 10 2

输出

136

题解

经典的贪心+并查集维护天数的板子。
−i2∗ci不变,要使ti2∗ci最大,则按ci大到小排序后,每次选择能选的最小的ti即可。

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.io.StreamTokenizer;
import java.util.*;

public class Main {
    static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
    static StreamTokenizer cin = new StreamTokenizer(br);

    static PrintWriter out = new PrintWriter(System.out);
    static int nextInt() throws Exception {
        cin.nextToken();
        return (int) cin.nval;
    }
    static String next() throws Exception {
        cin.nextToken();
        return cin.sval;
    }
    static double nextDouble() throws Exception {
        cin.nextToken();
        return cin.nval;
    }
    public static void main(String[] args) throws Exception {
        int n = nextInt(), k = nextInt();
        int[][] c = new int[n + 1][2];
        p = new int[n + 2];
        long res = 0;

        for (int i = 1; i <= n; i++) {
            c[i][0] = i;
            c[i][1] = nextInt();
            res -= (long) i * i * c[i][1];

        }
        for (int i = 0; i < p.length; i++) {
            p[i] = i;
        }
        Arrays.sort(c, 1, c.length, (o1, o2) -> o2[1] - o1[1]);
        for (int i = 1; i <= n; i++) {
            int di = c[i][0];
            int ci = c[i][1];
            int sub = di - k;
            if (sub < 1) {
                sub = 1;
            }
            int day = find(sub);
            p[day] = find(day + 1);
            day += k;
            res += (long) day * day * ci;
        }
        System.out.println(res);
    }
    static int[] p;

    static int find(int x) {
        if (p[x] != x) {
            p[x] = find(p[x]);
        }
        return p[x];
    }
}

BD202203简单题

题目

小度喜欢单调序列。一天,小度得到了一个序列,他决定把这个序列拆分成两个序列:一个是(非严格)上升序列,一个是(非严格)下降序列。小度猪脑过载了,你能帮帮他吗?
已知一个正整数序列a1…an,你需要判断能否将a序列恰好划分成两个非空子序列(a序列中的每个元素属于且仅属于其中一个子序列),使得一个是(非严格)上升序列,另一个是(非严格)下降序列。

格式

输入格式

输入的第一行为一个整数n,表示序列长度。
输入第二行为n个整数ai

输出格式

输出仅一行,如果存在合法的划分方案,输出”yes”(不含引号),否则输出”no”(不含引号)。

数据范围

n ≤ 105, 1 ≤ ai​ ≤ 109

样例

输入

6
1 1 4 5 1 4

输出

yes

题解

动态规划即可,f0[i]表示把a[i]添加进递增序列时,递减序列的限制。f1[i]表示把a[i]添加进递减序列时,递增序列的限制.。

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.io.StreamTokenizer;
import java.util.*;

public class Main {
    static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
    static StreamTokenizer cin = new StreamTokenizer(br);

    static PrintWriter out = new PrintWriter(System.out);
    static int nextInt() throws Exception {
        cin.nextToken();
        return (int) cin.nval;
    }
    static String next() throws Exception {
        cin.nextToken();
        return cin.sval;
    }
    static double nextDouble() throws Exception {
        cin.nextToken();
        return cin.nval;
    }
    public static void main(String[] args) throws Exception {
        int n = nextInt();
        int[] f0 = new int[n + 1];
        int[] f1 = new int[n + 1];
        int[] a = new int[n + 1];
        for (int i = 1; i <= n; i++) {
            a[i] = nextInt();
        }
        Arrays.fill(f0, 0);
        Arrays.fill(f1, Integer.MAX_VALUE);
        f0[0] = Integer.MAX_VALUE;
        f1[0] = 0;
        for (int i = 1; i <= n; i++) {
            a[0] = 0;
            if (a[i] >= a[i - 1]) {
                f0[i] = f0[i - 1];
            }
            if (a[i] >= f1[i - 1]) {
                a[0] = Integer.MAX_VALUE;
                f0[i] = Math.max(f0[i], a[i - 1]);
            }
            a[0] = Integer.MAX_VALUE;
            if (a[i] <= a[i - 1]) {
                f1[i] = f1[i - 1];
            }
            if (a[i] <= f0[i - 1]) {
                a[0] = 0;
                f1[i] = Math.min(f1[i], a[i - 1]);
            }
        }
        System.out.println(f1[n] != Integer.MAX_VALUE || f0[n] != 0 ? "yes" : "no");
    }
}


未完待续…以后还会更新剩下的题目。

最近比较忙,可能停更一段时间。