zoukankan      html  css  js  c++  java
  • Atcoder ARC-061

    ARC061(2020.7.10)

    A

    暴力 (dfs) 即可。

    B

    考虑统计以每个点为矩阵中心的答案,显然一个黑点只会影响周围九个黑点,使用 (map) 来记录这个值,每次修改修改一下答案数组即可。

    C

    首先是一个非常直接的做法,我们可以类似 (dijkstra) 的做法,使用堆来维护一个一个答案为第一关键字,种类为第二关键字的结构体,那么进入堆中的点将不会超过 (n + m) 个,每次再类似 (dijkstra) 的方法拓展即可。

    另一种做法是这样的,因为我们只有在换乘的时候答案会 (+1) 那么我们可以考虑对同种类的道路连起来成为一条新路使得路径上的边权为 (0) 而进入这条道路的费用为 (1) 即可。不难发现我们可以将每个点拆乘 (c) (道路种数) 个点,分别表示在某个种类的新道上,从这个点向拆出来的点连一条边权为 (1) 的边表示进入这个种类的新路,对于原图中的每一条边,我们在其两端点对应种类拆出来的点连一条边权为 (0) 的边,那么这样就能达到我们最开始的想法,最后 (frac{dis_n}{2}) 就是答案了,因为是双向边,进入和出来都会花费代价。但我们会发现点数是 (O(nc)) 级别的,但实际上边数很少是 (O(n + m)) 的,那么这就意味着很多点是没有用的,那么我们把这些没有用的点去掉剩下的点数就可以达到 (O(n + m)) 级别,于是复杂度就是 (O((n + m) log n)) 了。

    D

    首先不难发现要让 (A) 赢需要 (A)(n + 1) 张牌,加上 (A) 最开始拿走的一张牌,意味着要抽出 (A) 号牌 (n) 张,因此在抽牌过程中最多抽 (B) 号牌 (m) 张,(C) 号牌 (k) 张。显然直接按照题目模拟并不好做,于是我们考虑这个抽牌的序列。不难发现一个抽牌序列(将所有牌抽完)会与摆放方案一一对应,于是我们考虑直接对抽牌序列进行计数即可。根据前面的条件我们一定要让这个序列有 (n)(A),在序列中最多 (m)(B),最多 (k)(C).于是我们计数时可以考虑枚举 (A) 赢之前抽出了 (i)(B),先往 (n)(A) 中先插入 (i)(B),这一部分的方案为 (dbinom{n + i - 1}{i}),同理抽出 (j)(C) 对答案的贡献是 (dbinom{n + i + j - 1}{j}).最后剩下的牌可以任意排列即 (3 ^ {m + k - i - j}),那么最终的答案可以写成:

    [sumlimits_{i = 0} ^ m sumlimits_{j = 0} ^ k dbinom{n + i - 1}{i} dbinom{n + i + j - 1}{j} 3 ^ {m + k - i - j} ]

    中间有很多 (i + j) 的项,方便起见我们枚举 (S = i + j)

    [sumlimits_{S = 0} ^ {m + k} 3 ^ {m + k - S} sumlimits_{i = max(0, S - k)} ^ m dbinom{n + i - 1}{i} dbinom{n + S - 1}{j} ]

    现在想办法把后面的东西化简,因为我们是从组合意义推来的组合数,因此再考虑组合意义不太可能,一个常见的套路是将组合数拆开看能否约去一些数。下面单独将后面那一坨拉出来看。

    [sumlimits_{i = max(0, S - k)} ^ m frac{(n + i - 1)!}{i! (n - 1)!} imes frac{(n + S - 1)!}{j! (n + i - 1)!} ]

    ((n + i - 1)!) 约去

    [sumlimits_{i = max(0, S - k)} ^ m frac{1}{i! (n - 1)!} imes frac{(n + S - 1)!}{j!} ]

    可以发现下面三项加起来等于上面的数,根据一个常见的套路对于 (frac{(a + b + c)!}{a! b! c!}) 这种柿子,我们可以将下面任意两项单独拉出来上下同乘两个数之和变成两个组合数相乘。比如可以将上面的式子画成这样:

    [sumlimits_{i = max(0, S - k)} ^ m frac{S!}{i! j!} imes frac{(n + S - 1)!}{S! (n - 1)!} ]

    [sumlimits_{i = max(0, S - k)} ^ m dbinom{S}{i} imes dbinom{n + S - 1}{S} ]

    于是后面这个东西就之和 (S) 有关了,拉到前面去就变成了:

    [sumlimits_{S = 0} ^ {m + k} 3 ^ {m + k - S} dbinom{n + S - 1}{S} sumlimits_{i = max(0, S - k)} ^ m dbinom{S}{i} ]

    可以发现后面相当于要求一行组合数的前缀和,这个东西是没办法快速计算的,但同时我们发现只需要查询两点的前缀和,其中一个右端点即在第 (i) 行前 (m) 个数的前缀和,而 (m) 是一个定值,可以考虑从之前的答案递推过来,令 (f_i) 为第 (i) 行前 (m) 个数的前缀和,再考虑组合数的递推公式 (dbinom{n}{m} = dbinom{n - 1}{m - 1} + dbinom{n - 1}{m})。将当前行的贡献算到上一行去,不难发现上一行的前 (m - 1) 个数要算 (2) 遍,最后一个数要算 (1) 遍,那么我们可以得到递推式 (f_i = 2 imes f_{i - 1} - dbinom{i - 1}{m}).再考虑其左端点查询的位置,令 (g_i) 为第 (i) 行前 (max(0, S - k) - 1) 的前缀和,不难发现最开始一端后面那个式子是从 (0) 开始枚举的,那么不需要减去左端点的值,而在 (S > k) 的时候,后面那一段就不再从 (0) 开始枚举了,看起来我们每次需要算每一行的前缀和位置不同,但实际上随着 (S) 的不断 (+1) 实际上 (S - k) 每次也只增加一位,也就意味着我们这一次计算的前缀和只和上一次有一位的区别,那么我们只需要手动加上这一位的答案即可,类似于前面的递推,可以得到 (g_i = 2 imes g_{i - 1} + dbinom{i - 1}{i - k - 1}).于是枚举 (S) 后面那个东西就可以 (O(1)) 算了。

    GO!
  • 相关阅读:
    Deploy Django in Windows
    Django开发环境配置
    springmvc源码解读
    分布式事务中常见的三种解决方案
    分布式锁总结
    JAVA代码优化
    Mysql(mysql总结)
    树(数据结构二)
    集合(Java总结一)
    centos7.7下docker与k8s安装(DevOps三)
  • 原文地址:https://www.cnblogs.com/Go7338395/p/13675916.html
Copyright © 2011-2022 走看看