前言

alt


整体评价

D是一道数学题,E是一道经典的入门树形DP,F题是一道期望DP,记忆化的方式更加简单一些。

ABC虽然偏简单,但是都是构造形态的,好像有CF风格了。


欢迎关注

珂朵莉 牛客周赛专栏

珂朵莉 牛客小白月赛专栏


A. 小红的删字符

思路: 模拟

题意指定长度为3

s = input()
print (s[0] + s[2])

B. 小红的正整数

思路: 贪心 + 构造

先选择一个非零的数,然后按自然序构造即可

from collections import Counter

x = input()
cnt = Counter(x)

res = []
for i in range(1, 10):
    k = chr(i + ord('0'))
    if cnt[k] > 0:
        res.append(k)
        cnt[k] -= 1
        break
        
for i in range(0, 10):
    k = chr(i + ord('0'))
    # repeat k
    res.extend(k * cnt[k])
        
print (''.join(res))

C. 小红构造回文

思路: 寻找不同的字符

限定在前半段(不包含奇数长度的中心节点)

然后交换即可

s = input()

s = [c for c in s]
n = len(s)

p = -1
for i in range(1, n//2):
    if s[0] != s[i]:
        p = i
        break

# not found        
if p == -1:
    print (-1)
else:
    # swap
    s[0], s[p] = s[p], s[0]
    s[n - 1], s[n - 1 - p] = s[n - 1 - p], s[n - 1]
    print (''.join(s))

D. 小红整数操作

思路: 数学

先求a,b的最小形态,即皆除以最大公约数gcd,得到

c = a/gcd(a,b)

d = b/gcd(a,b)

假定

然后就是数学中的范围收敛知识了

最小倍数l = (x + c - 1) / c

最大倍数r = y / d

然后求[l, r]的区间个数,即为答案

import java.io.*;
import java.util.*;

public class Main {

    static int gcd(int a, int b) {
        return b == 0 ? a : gcd(b, a % b);
    }

    public static void main(String[] args) {
        Scanner sc = new Scanner(new BufferedInputStream(System.in));

        int a = sc.nextInt(), b = sc.nextInt();
        int x = sc.nextInt(), y = sc.nextInt();

        // swap
        if (a > b) {
            int t = a;
            a = b;
            b = t;
        }

        int g = gcd(a, b);
        int c = a/g;
        int d = b/g;

        // 范围组合
        int d1 = (x + c - 1) / c;
        int d2 = y / d;

        if (d1 <= d2) {
            System.out.println(d2 - d1 + 1);
        } else {
            System.out.println(0);
        }
    }

}

E. 小红树上染色

思路: 树形DP

每个节点有两种状态, 为白色/红色

因为限制是不能存在两个相邻节点为白色

所以如果当前节点为白色,其子节点必须都是红色节点

如果当前节点为红色,其子节点红白即可

状态叠加是基于乘法原理的

注: 这边是+号

import java.io.*;
import java.util.*;

public class Main {

    static long mod = (long)1e9 + 7;

    static class Solution {
        int n;
        List<Integer> []g;
        long[] white;
        long[] red;

        long solve(int n, List<Integer> []g) {
            this.n = n;
            this.g = g;

            this.white = new long[n];
            this.red = new long[n];
            dfs(0, -1);
            return (white[0] + red[0]) % mod;
        }

        void dfs(int u, int fa) {
            long w = 1, r = 1;
            for (int v: g[u]) {
                if (v == fa) continue;
                dfs(v, u);

                w = w * red[v] % mod;
                r = r * ((white[v] + red[v]) % mod) % mod;
            }
            white[u] = w;
            red[u] = r;
        }

    }

    public static void main(String[] args) {
        Scanner sc = new Scanner(new BufferedInputStream(System.in));
        // 树形DP
        int n = sc.nextInt();
        List<Integer>[]g = new List[n];
        Arrays.setAll(g, x->new ArrayList<>());
        for (int i = 0; i < n - 1; i++) {
            int u = sc.nextInt() - 1 , v = sc.nextInt() - 1;
            g[u].add(v);
            g[v].add(u);
        }
        Solution solution = new Solution();
        long r = solution.solve(n, g);
        System.out.println(r);
    }

}

F. 小红叒战小紫

思路: 期望DP

构建4维DP,记忆化搜索

关键在于

  • 状态压缩
  • 退出条件
  • 期望E的推导公式
import java.io.BufferedInputStream;
import java.math.BigInteger;
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;

public class Main {

    static long mod = (long)1e9 + 7;

    static class Solution {

        Long[][][][] opt;

        long solve(int n1, int n2, int m1, int m2) {
            opt = new Long[n1 + 1][n2 + 1][m1 + 1][m2 + 1];
            return dfs(n1, n2, m1, m2);
        }

        long tx(long a) {
            return (a % mod + mod) % mod;
        }

        long dfs(int s1, int s2, int e1, int e2) {
            if (s1 == -1 || e1 == -1) return tx(-1);

            if (s1 + s2 == 0) return 0;
            if (e1 + e2 == 0) return 0;
            if (s1 == 0 && e1 == 0) return 0;
            if (s2 == 0 && e2 == 0) return 0;
            if (s1 == 0 && s2 > 0 && e2 == 0) return e1;
            if (e2 > 0 && e1 == 0 && s2 == 0) return s1;

            if (opt[s1][s2][e1][e2] != null) return opt[s1][s2][e1][e2];

            int p1 = s1 + s2;
            int p2 = e1 + e2;

            long r1 = dfs(s1 - 1, s2, e1, e2);
            r1 = tx(r1);
            r1 = r1 * s1 % mod * inv(p1) % mod * e2 % mod * inv(p2) % mod;

            long r2 = dfs(s1, s2, e1 - 1, e2);
            r2 = tx(r2);
            r2 = r2 * s2 % mod * inv(p1) % mod * e1 % mod * inv(p2) % mod;

            long r = (r1 + r2 + 1) % mod;
            r = tx(r);

            r = r * p1 % mod * p2 % mod * inv(tx(tx(tx(p1 * p2) - tx(s1 * e1)) - tx(s2 * e2))) % mod;

            return opt[s1][s2][e1][e2] = r;
        }

        // 逆元构建
        Map<Long, Long> memo = new HashMap<>();
        long inv(long v) {
            if (memo.containsKey(v)) return memo.get(v);
            long r = BigInteger.valueOf(v).modInverse(BigInteger.valueOf(mod)).longValue();
            memo.put(v, r);
            return r;
        }

    }

    public static void main(String[] args) {
        Scanner sc = new Scanner(new BufferedInputStream(System.in));

        int n = sc.nextInt(), m = sc.nextInt();
        int n1 = 0, n2 = 0;
        int m1 = 0, m2 = 0;

        for (int i = 0; i < n; i++) {
            int v = sc.nextInt();
            if (v == 1) n1++;
            else n2++;
        }
        for (int i = 0; i < m; i++) {
            int v = sc.nextInt();
            if (v == 1) m1++;
            else m2++;
        }

        Solution solution = new Solution();
        long r = solution.solve(n1, n2, m1, m2);
        System.out.println(r);
    }

}

写在最后

alt