[LeetCode周赛复盘] 第 364 场周赛20230924

一、本周周赛总结

  • T4出思路挺快的,但是卡了好久,换根题还是有点恐惧。
  • T1 贪心。
  • T2 枚举。
  • T3 单调栈+前后缀分解DP。
  • T4 换根DP/DSU+乘法原理。
    在这里插入图片描述

8048. 最大二进制奇数

8048. 最大二进制奇数

1. 题目描述

在这里插入图片描述

2. 思路分析

  • 保证一个1在最后,把剩下的提前。

3. 代码实现

class Solution:
    def maximumOddBinaryNumber(self, s: str) -> str:
        cnt = s.count('1')
        n = len(s)
        return '1'*(cnt-1)+'0'*(n-cnt)+'1'

100048. 美丽塔 II、100049. 美丽塔 I

100048. 美丽塔 II
100049. 美丽塔 I

1. 题目描述

在这里插入图片描述

2. 思路分析

 t2可以平方,那么枚举每个位置作为最大值,前后扩展即可。重点说说T3的O(n)单调栈做法。
  • 依然考虑枚举每个点作为最高点,前后缀分解,记pre[i]为以i为最高点时,前边的和。
  • 那么从i向前找,所有>a[i]的位置都是得填a[i]的,直到遇到一个<=a[i]的位置。记为p。
  • 显然pre[i] = pre[p]+(i-p)*a[i]。
  • 后缀同理计算。
  • 最后枚举每个位置计算前后缀,由于a[i]算了两次,要减去一次。

3. 代码实现

class Solution:
    def maximumSumOfHeights(self, a):
        n = len(a)
        pre = [0] * (n)
        suf = [0] * (n)
        st = []
        for i, v in enumerate(a):
            while st and v < a[st[-1]]:
                st.pop()
            if st:
                pre[i] = pre[st[-1]] + (i-st[-1])*v
            else:
                pre[i] = v*(i+1)
            st.append(i)
        st = []
        for i in range(n-1,-1,-1):
            v = a[i]
            while st and v < a[st[-1]]:
                st.pop()
            if st:
                suf[i] = suf[st[-1]] + (st[-1]-i)*v
            else:
                suf[i] = v*(n-i)
            st.append(i)
        ans = n
        for x,y,z in zip(pre,suf,a):
            ans = max(ans, x+y-z)

        return ans

100047. 统计树中的合法路径数目

100047. 统计树中的合法路径数目

1. 题目描述

在这里插入图片描述

2. 思路分析

  • 由于限制路径上只能有一个质数,那么考虑枚举每个质数,然后乘法原理计算方案数。
  • 考虑以2为中间点,那么两边的端点可以从2的任两个子树(分叉,这里显然要包含向上走的方向)中选。
  • 那么当计算出2相邻的所有分支中,能触达的合数个数,问题就转化成了:计算a中所有二元组乘积 的和。可以用状态机dp做。
  • 注意,本题路径不能只含一个点,因此分开计算端点为当前质数节点的方案数,即加上每个分支size即可。

  • 那么怎么计算触达合数呢,可以换根dp。
  • 我们发现所有能触达的合数其实是相连的,而且数量汇聚到了父节点(第一次dfs),那么把子节点直接赋值成父节点即可。
  • 也可以遍历边来DSU,取最大即可。

3. 代码实现

class PrimeTable:
    def __init__(self, n: int) -> None:
        self.n = n
        self.primes = primes = []  # 所有n以内的质数
        self.min_div = min_div = [0] * (n + 1)  # md[i]代表i的最小(质)因子
        min_div[1] = 1

        # 欧拉筛O(n),顺便求出min_div
        for i in range(2, n + 1):
            if not min_div[i]:
                primes.append(i)
                min_div[i] = i
            for p in primes:
                if i * p > n: break
                min_div[i * p] = p
                if i % p == 0:
                    break


    def is_prime(self, x: int):
        """检测是否是质数,最坏是O(sqrt(x)"""
        if x < 3: return x == 2
        if x <= self.n: return self.min_div[x] == x
        for i in range(2, int(x ** 0.5) + 1):
            if x % i == 0: return False
        return True

    def prime_factorization(self, x: int):
        """分解质因数,复杂度
        1. 若x>n则需要从2模拟到sqrt(x),如果中间x降到n以下则走2;最坏情况,不含低于n的因数,则需要开方复杂度
        2. 否则x质因数的个数,那么最多就是O(lgx)"""
        n, min_div = self.n, self.min_div
        for p in range(2, int(x ** 0.5) + 1):
            if x <= n: break
            if x % p == 0:
                cnt = 0
                while x % p == 0: cnt += 1; x //= p
                yield p, cnt
        while 1 < x <= n:
            p, cnt = min_div[x], 0
            while x % p == 0: cnt += 1; x //= p
            yield p, cnt
        if x >= n and x > 1:
            yield x, 1

    def get_factors(self, x: int):
        """求x的所有因数,包括1和x"""
        factors = [1]
        for p, b in self.prime_factorization(x):
            n = len(factors)
            for j in range(1, b + 1):
                for d in factors[:n]:
                    factors.append(d * (p ** j))
        return factors

    def mr_is_prime(self, x):
        """
        Miller-Rabin 检测. 检测x是否是质数,置信度: 1 - (1/4)^k. 复杂度k*log^3
        但是longlong以内可以用k==3或7的代价,换取100%置信度
        https://zhuanlan.zhihu.com/p/349360074
        """
        if x < 3 or x % 2 == 0:
            return x == 2
        if x % 3 == 0:
            return x == 3

        u, t = x - 1, 0
        while not u & 1:
            u >>= 1
            t += 1
        ud = (2, 325, 9375, 28178, 450775, 9780504, 1795265022)  # long long 返回用这个7个数检测100%正确
        # ud = (2, 7, 61)  # int 返回用这3个数检测100%正确
        # for _ in range(k):
        #     a = random.randint(2, x - 2)
        for a in ud:
            v = pow(a, u, x)
            if v == 1 or v == x - 1 or v == 0:
                continue
            for j in range(1, t + 1):
                v = v * v % x
                if v == x - 1 and j != t:
                    v = 1
                    break
                if v == 1:
                    return False
            if v != 1:
                return False
        return True

# pt = PrimeTable(1000)
#
# # 求质数
primes = set(PrimeTable(10 ** 5+10).primes)
class Solution:
    def countPaths(self, n: int, edges: List[List[int]]) -> int:
        g = [[] for _ in range(n)]
        for u,v in edges:
            g[u-1].append(v-1)
            g[v-1].append(u-1)
        
        f = [0] * n
        def dfs(u,fa):
            if (u+1) not in primes:
                f[u] += 1
            for v in g[u]:
                if v != fa:
                    dfs(v, u)
                    if (u+1) not in primes:
                        # if u == 0:
                        #     print(f)
                        f[u] += f[v]
        def reroot(u,fa):
            for v in g[u]:
                if v != fa:
                    if (v+1) not in primes and (u+1) not in primes:
                        f[v] = f[u] 
                    reroot(v, u)
        dfs(0,-1)
        reroot(0,-1)
        ans = 0
        for u in range(n):        
            if (u+1) in primes:
                p = 0
                for v in g[u]:
                    if (v+1) not in primes:
                        ans += (f[v]) * p + f[v]
                        p += f[v]
        return ans         

参考链接

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值