Bootstrap

Atcoder Beginner Contest 350 A~E Solution 题解


Note: 省略模版代码

A - Past ABCs

题目:
给一个以 ABC 开头的长度为6的字符串,最后三个字符是数字,要求数字大于0且小于350,不包含316

思路:
取子字符串判断即可

static void solve() throws IOException {
    String ins = in.readLine();
    int n = pInt(ins.substring(3));
    if (n > 0 && n < 350 && n != 316) {
        out.println("Yes");
    } else {
        out.println("No");
    }
}

B - Dentist Aoki

题目:
Takahashi有N个牙齿,从1到N标号,牙医Aok执行Q次治疗:

  • 如果牙洞上有牙齿,拔掉
  • 如果牙洞上没有牙齿,则种一颗牙

求Q次治疗之后Takahashi有多少个牙齿

吐槽:好残忍啊

思路:
记录一下是否有牙齿,实时维护数量即可

static void solve() throws IOException {
    String[] ins = pStringArray();
    int n = pInt(ins[0]), q = pInt(ins[1]);
    int ans = n;
    int[] op = pIntArray(0);
    boolean[] vis = new boolean[n + 1];
    for (int i = 0; i < q; i ++) {
        if (vis[op[i]]) {
            ans ++;
            vis[op[i]] = false;
        } else {
            ans --;
            vis[op[i]] = true;
        }
    }
    out.println(ans);
}

C - Sort

题目:
给一个从1到N的排列A,执行若干次(0与N-1次之间)操作,每次操作选取一对下标进行交换,要求操作执行完后A为单调递增,
输出所选取的下标对

思路:
对于当前位置 i,对应的值肯定是 i,如果不是 i 那么就需要将当前位置上的 A[i] 放到其应该回到的位置(下标为A[i])

static void solve() throws IOException {
    int n = pInt(in.readLine());
    int[] p = pIntArray(1);
    List<int[]> ans = new ArrayList<>();
    for (int i = 1; i <= n; i ++) {
        while (p[i] != i) {
            if (p[i] < i) {
                ans.add(new int[] {p[i], i});
            } else {
                ans.add(new int[] {i, p[i]});
            }
            int t = p[i];
            p[i] = p[p[i]];
            p[t] = t;
        }
    }
    out.println(ans.size());
    for (int[] x : ans) {
        out.println(x[0] + " " + x[1]);
    }
}

D - New Friends

题目:
有N个用户,编号1到N,两个用户可以成为朋友,朋友关系是双向(无向边),现在有M对朋友关系,求能够执行以下操作的最大次数:选取三个用户,X和Y是朋友,Y和Z是朋友,Z和X不是朋友,使X和Z成为朋友;

思路:
首先,通过样例能够知道,执行完操作后,当前连通块会成为一个完全连通图;那么当前连通块的最大执行次数就是:完全连通图的边数减去当前连通块的边数;完全连通块的边数为(连通块的点数为x) x ⋅ ( x − 1 ) 2 \frac{x \cdot (x - 1)}{2} 2x(x1)

那使用 并查集 维护每个连通块的结点数以及边数,就能够求出每个连通块的最大执行次数,最后将这些加起来就行了

// 并查集
static class UnionFind {
    final int[] p;
    // 结点数量
    final int[] cnt;
    // 边数量
    final long[] edges;
    public UnionFind(int n) {
        p = new int[n];
        cnt = new int[n];
        edges = new long[n];
        Arrays.fill(cnt, 1);
        Arrays.setAll(p, idx -> idx);
    }
    public int find(int x) {
        if (p[x] != x) {
            p[x] = find(p[x]);
        }
        return p[x];
    }
    public void merge(int x, int y) {
        int px = find(x), py = find(y);
        edges[py] += 1;
        if (px != py) {
            p[px] = py;
            cnt[py] += cnt[px];
            edges[py] += edges[px];
        }
    }
    public int getCnt(int x) {
        return cnt[find(x)];
    }
    public long getEdgeCnt(int x) {
        return edges[find(x)];
    }
}

static void solve() throws IOException {
    String[] ins = pStringArray();
    int n = pInt(ins[0]), m = pInt(ins[1]);
    UnionFind uf = new UnionFind(n + 1);
    while (m -- > 0) {
        ins = pStringArray();
        int u = pInt(ins[0]), v = pInt(ins[1]);
        uf.merge(u, v);
    }
    long ans = 0;
    Set<Integer> vis = new HashSet<>();
    for (int i = 1; i <= n ; i ++) {
        if (!vis.contains(uf.find(i))) {
            vis.add(uf.find(i));
            int cnt = uf.getCnt(i);
            long count = (long) cnt * (cnt - 1) / 2;
            ans += count - uf.getEdgeCnt(uf.find(i));
        }
    }
    out.println(ans);
}

E - Toward 0

题目:
给一个整数N,可以执行两种操作:

  • 将N替代为 ⌊ N A ⌋ \left \lfloor \frac{N}{A} \right \rfloor AN,代价为X
  • 投6面的骰子的结果为B,将N替代为 ⌊ N B ⌋ \left \lfloor \frac{N}{B} \right \rfloor BN,代价为Y

求能够使N变成0最小的期望代价;

思路:
N替代为 ⌊ N A ⌋ \left \lfloor \frac{N}{A} \right \rfloor AN 时是一种状态转化,可以规定一种状态:f(n) 为从 n 变成 0 的最小期望代价;
状态初始:f(0) = 0 (0已经是目标值,所以代价为0);

  • 操作1: r e s 1 = X + f ( ⌊ N A ⌋ ) res_1= X + f(\left \lfloor \frac{N}{A} \right \rfloor) res1=X+f(AN)
  • 操作2: r e s 2 = ∑ i = 1 6 ( f ( ⌊ N i ⌋ ) + y ) / 6 res_2=\sum^{6}_{i=1}{(f(\left \lfloor \frac{N}{i} \right \rfloor) + y)} / 6 res2=i=16(f(iN)+y)/6

那么 f ( N ) f(N) f(N) 的最小期望代价为 m i n ( r e s 1 , r e s 2 ) min(res_1, res_2) min(res1,res2)

做一下记忆化处理

// 记忆化
static Map<Long, Double> st = new HashMap<>();
static double dfs(long a, long x, long y, long u) {
    Double v = st.getOrDefault(u, null);
    if (v != null) return v;
    if (u == 0) {
        return 0.0;
    }
    // 操作1的结果
    double res1 = dfs(a, x, y, u / a) + x;
    // 操作二的结果
    double res2 = 0;
    // 这样写会死循环,因为当i=1时会循环
      for (int i = 1; i <= 6; i ++) {
          res2 += (dfs(a, x, y, u / i) + y) / 6;
      }
    // 假设投骰子不会投到1,设置代价为 y * 6/5
    res2 += (double) (y * 6) / 5;
    for (int i = 2; i <= 6; i ++) {
        res2 += dfs(a, x, y, u / i) / 5;
    }
    double res = Math.min(res1, res2);
    st.put(u, res);
    return res;
}
static void solve() throws IOException {
    String[] ins = pStringArray();
    long n = pLong(ins[0]), a = pLong(ins[1]), x = pLong(ins[2]), y = pLong(ins[3]);
    out.printf("%.10f", dfs(a, x, y, n));
}

模板

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.nio.file.Files;
import java.nio.file.Paths;

public class Example {

    static void solve() throws IOException {

    }

    public static void main(String[] args) throws IOException {
        int t = 1;
//        t = pInt(in.readLine());
        while (t-- > 0) {
            solve();
        }
        out.flush();
        out.close();
        in.close();
    }


    static InputStreamReader input = null;

    static {
        try {
            input = new InputStreamReader(Files.newInputStream(Paths.get("D:\\Project\\Problem\\tianti\\src\\in.txt")));
        } catch (IOException e) {
            input = new InputStreamReader(System.in);
        }
    }

    static final BufferedReader in = new BufferedReader(input);
    static final PrintWriter out = new PrintWriter(System.out);

    static String[] pStringArr() throws IOException {
        return in.readLine().split(" ");
    }

    static int pInt(String s) {
        return Integer.parseInt(s);
    }

    static long pLong(String s) {
        return Long.parseLong(s);

    }

    static int[] pIntArr(int idx) throws IOException {
        String[] ins = pStringArr();
        int[] res = new int[ins.length + idx];
        for (int i = 0, j = idx; i < ins.length; i++, j++) {
            res[i] = Integer.parseInt(ins[j]);
        }
        return res;
    }

    static long[] pLongArr(int idx) throws IOException {
        String[] ins = pStringArr();
        long[] res = new long[ins.length + idx];
        for (int i = 0, j = idx; i < ins.length; i++, j++) {
            res[i] = Long.parseLong(ins[j]);
        }
        return res;
    }


}
;