• Edu Codeforce133 (D、F) DP、组合数学


    D题 Chip Move

    题意:有一条数轴,初始位于 0 0 0位置,给定整数 n , k ( 1 ≤ k , n ≤ 2 ⋅ 1 0 5 ) n,k(1\le k,n\le 2·10^5) n,k(1k,n2105),第 i i i次的移动距离仅能为 k + i k+i k+i的倍数,对于每个正整数 x x x,求走到 x x x这个点的方案数,其中 x ∈ [ 1 , n ] x\in[1,n] x[1,n]

    • 为了方便陈述,若当前轮数要求的走的长度为 k + i k+i k+i的倍数,我们称其为第 k + i k+i k+i轮,而非第 i i i

    • 先考虑一个最暴力的做法,设状态 d p [ i ] [ j ] dp[i][j] dp[i][j]表示,表示走 1 , 2 , ⋯   , i 1,2,\cdots,i 1,2,,i步能到位置 j j j的方案数的总和,显然初始状态位于 0 0 0位置,仅一种方案,即 d p [ k − 1 ] [ 0 ] = 1 dp[k-1][0]=1 dp[k1][0]=1

    • 考虑当前位置 j j j,逆向思维枚举上一步走的距离为 i i i的倍数,也就是反向跳 d × i d\times i d×i,其中 j − d × i ≥ 0 j-d\times i\ge 0 jd×i0,故 1 ≤ d ≤ ⌊ j i ⌋ 1\le d \le \lfloor \frac{j}{i}\rfloor 1dij的容易得到转移方程 d p [ i ] [ j ] = ∑ d = 1 ⌊ j i ⌋ d p [ i − 1 ] [ j − d × i ] dp[i][j]=\sum_{d=1}^{\lfloor \frac{j}{i}\rfloor} dp[i-1][j-d\times i] dp[i][j]=d=1ijdp[i1][jd×i]

    • 此时我们对于每轮每个位置的状态转移,最终需要 O ( n 3 ) O(n^3) O(n3)的时间复杂度

    • 往前回跳距离 i i i,我们发现对于 j − i j-i ji的状态转移方程为 d p [ i ] [ j − i ] = ∑ d = 1 ⌊ j − i i ⌋ d p [ i − 1 ] [ j − i − d × i ] dp[i][j-i]=\sum_{d=1}^{\lfloor \frac{j-i}{i}\rfloor} dp[i-1][j-i-d\times i] dp[i][ji]=d=1ijidp[i1][jid×i]

    • 对比上下两个转移方程,我们发现方程右部仅相差一个数 d p [ i ] [ j − i ] dp[i][j-i] dp[i][ji],故我们可以用一个前缀和来优化这个转移,即对于一个同余系 j   m o d   i j \bmod i jmodi,对应一个前缀和 t p [ j   m o d   i ] tp[j \bmod i] tp[jmodi],然后每轮预处理一下这个同余前缀和,即可将时间复杂度降为 O ( n 2 ) O(n^2) O(n2)

      此处类似完全背包问题同余系优化的思路

    • 但是我们发现,此时的时间复杂度,对于 n n n的范围来说,还是不能接受的。于是我们再到题意中寻找某些性质,祈求能找到数据的某些特殊性质

    • 有如下性质:题目中的轮数,最多仅有 O ( n ) O(\sqrt n) O(n )轮,即外层循环 i i i最大仅为 n \sqrt n n ,考虑最差情况 k = 1 k=1 k=1,每次跳的倍数 d = 1 d=1 d=1时,走了 i i i轮的情况,最后的距离为等差数列求和 [ 1 + ( 1 + i ) ] i 2 \cfrac{[1+(1+i)]i}{2} 2[1+(1+i)]i,发现其为 O ( n 2 ) O(n^2) O(n2)级别,即走的距离随着轮数,呈至少 Ω ( n 2 ) \Omega(n^2) Ω(n2)级别增长,故走完距离为 n n n也只需要 O ( n ) O(\sqrt n) O(n )

    • 这样我们就将时间复杂度降为 O ( n n ) O(n\sqrt n) O(nn )了,足矣AC此题

    • 最后考虑空间复杂度,我们发现第 i i i轮的状态仅跟第 i − 1 i-1 i1轮的有关,故可以使用滚动数组优化掉一维,这样我们的空间复杂度就可以降到 O ( n ) O(n) O(n),我们用 l a s t last last数组表示上一轮的状态

    //其中Z表示模数类,运算时自动取余998244353,可参照jiangly代码
    #define rep(i, a, b) for (int i = (a); i <= (b); i++)
    int main()
    {
        int n, k;
        cin >> n >> k;
        vector<Z> last(n + 1);
        last[0] = 1;
        vector<Z> ans(n + 1);
        rep(i, k, n)
        {
            vector<Z> dp(n + 1);
            vector<Z> tp(i);
            rep(j, i, n)
            {
                if(j - i >= 0)
                    tp[j % i] += last[j - i];//同余系
                dp[j] = tp[j % i];
            }
            rep(j, 1, n)
                ans[j] += dp[j];
            if(all_of(dp.begin(), dp.end(), [&](const Z &a){ //是否本轮全部状态都为0
                return !a.val();
            })) break;
            last = dp; //滚动数组优化
        }
        rep(i, 1, n)
            cout << ans[i] << ' ';
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    F题 Bags with Balls

    题意

    给定 n ( 1 ≤ n ≤ 998244352 ) n(1\le n \le 998244352) n(1n998244352)个袋子,每个袋子中都有 m ( 1 ≤ m ≤ 998244352 ) m(1\le m \le 998244352) m(1m998244352)个球,且其中每个袋子中的球都由 1 ∼ m 1\sim m 1m编号。从每个袋子中各取出一个球,最终会得到 n n n个球,在这些球中,设奇数球的个数为 F F F。求解所有可能方案得到的 F k ( 1 ≤ k ≤ 2000 ) F^k(1\le k \le 2000) Fk(1k2000)的总和。

    输入

    T ( 1 ≤ T ≤ 5000 ) T(1\le T \le5000) T(1T5000)组数据,每组读入三个数 n , m , k ( 1 ≤ n , m ≤ 9982443521 ≤ k ≤ 2000 ) . n,m,k(1≤n,m≤9982443521≤k≤2000). n,m,k(1n,m9982443521k2000).

    输出

    如题意所述,对答案取余 998244353 998244353 998244353

    • 容易得到,一个袋子中,奇数球的个数为 ⌊ m + 1 2 ⌋ \lfloor \cfrac{m+1}{2}\rfloor 2m+1,偶数球的个数为 ⌊ m 2 ⌋ \lfloor \cfrac{m}{2}\rfloor 2m

    • 考虑最裸的做法,有如下生成函数: ( ⌊ m 2 ⌋ + ⌊ m + 1 2 ⌋ x ) n (\lfloor \cfrac{m}{2}\rfloor+\lfloor \cfrac{m+1}{2}\rfloor x)^n (⌊2m+2m+1x)n

    • 二项式定理对生成函数进行化简有 ∑ i = 1 n C n i ⌊ m 2 ⌋ n − i ⌊ m + 1 2 ⌋ i x i \sum_{i=1}^n C_n^i\lfloor \cfrac{m}{2}\rfloor^{n-i}\lfloor \cfrac{m+1}{2}\rfloor^i x^i i=1nCni2mni2m+1ixi,此处我省略掉了 i = 0 i=0 i=0的情况,因为 x x x的系数为 i i i表示当前取了 i i i个奇数编号的球,显然取 0 0 0个奇数编号的球不会对我们答案有任何贡献,前面的系数表示的为方案数,故我们所求的答案为 ∑ i = 1 n C n i ⌊ m 2 ⌋ n − i ⌊ m + 1 2 ⌋ i i k \sum_{i=1}^n C_n^i\lfloor \cfrac{m}{2}\rfloor^{n-i}\lfloor \cfrac{m+1}{2}\rfloor^i i^k i=1nCni2mni2m+1iik

    • 考虑 n , m , k n,m,k n,m,k的范围,发现 n , m , k ( 1 ≤ n , m ≤ 9982443521 ≤ k ≤ 2000 ) n,m,k(1≤n,m≤9982443521≤k≤2000) n,m,k(1n,m9982443521k2000)。我们无法在 O ( n ) O(n) O(n)的时间内解决这个问题,所以我们尽量需要往 O ( k ) O(k) O(k)的方向靠

    • 考虑对 i k i^k ik进行展开,对于第二类斯特林数(斯特林子集数),根据二项式反演有如下结论: i k = ∑ c = 0 i S 2 ( k , c ) ( i c ) c ! i^{k}=\sum_{c=0}^{i} S_{2}(k, c)\binom{i}{c}c! ik=c=0iS2(k,c)(ci)c!

      • 第二类斯特林数 S 2 ( n , m ) S_2(n,m) S2(n,m)的含义为,将 n n n个数,划分为 m m m个集合的方案数
      • 考虑动态规划,对于一个新来的数,有两种方案,一种是已有 m m m个集合,从 m m m个集合中选择一个加入,另一种是这个数单独创建一个集合
      • 可以得到状态转移方程 S 2 ( n , m ) = S 2 ( n − 1 , m − 1 ) + S 2 ( n − 1 , m ) × m S_2(n,m)=S_2(n-1,m-1)+S_2(n-1,m)\times m S2(n,m)=S2(n1,m1)+S2(n1,m)×m
      • 在本题中,预处理第二类斯特林数的时间复杂度为 O ( k 2 ) O(k^2) O(k2)

    下列推导中, ( n i ) \binom{n}{i} (in)等价 C n i C_n^i Cni

    • 代入原式中可以得到

      ∑ i = 1 n ( n i ) ( ⌊ m 2 ⌋ ) n − i ( ⌊ m + 1 2 ⌋ ) i i k = ∑ i = 1 n ( n i ) ( ⌊ m 2 ⌋ ) n − i ( ⌊ m + 1 2 ⌋ ) i ( ∑ c = 0 i S 2 ( k , c ) ( i c ) c ! )

      i=1n(ni)(m2)ni(m+12)iik=i=1n(ni)(m2)ni(m+12)i(c=0iS2(k,c)(ic)c!)" role="presentation" style="position: relative;">i=1n(ni)(m2)ni(m+12)iik=i=1n(ni)(m2)ni(m+12)i(c=0iS2(k,c)(ic)c!)
      =i=1n(in)(⌊2m)ni(⌊2m+1)iiki=1n(in)(⌊2m)ni(⌊2m+1)i(c=0iS2(k,c)(ci)c!)

    • 然后我们尝试交换一下求和顺序,思路同莫比乌斯反演一样,先枚举出内层循环可能出现的值,然后对于每个内层循环,列出对应的外层循环的值

      此处为了方便化简计算又加上 0 0 0这个方案,因为斯特林数 S [ i ] [ 0 ] = 0 S[i][0]=0 S[i][0]=0故不影响,此处仅为了方便推导

      ∑ c = 0 min ⁡ ( k , n ) ∑ i = c n ( n i ) ⌊ m 2 ⌋ n − i ⌊ m + 1 2 ⌋ i S 2 ( k , c ) ( i c ) c ! = ∑ c = 0 min ⁡ ( k , n ) S 2 ( k , c ) ( n c ) c ! ∑ i = c n ( n − c i − c ) ⌊ m 2 ⌋ n − i ⌊ m + 1 2 ⌋ i = ∑ c = 0 min ⁡ ( k , n ) S 2 ( k , c ) ( n c ) c ! ⌊ m + 1 2 ⌋ c ∑ i = c n ( n − c i − c ) ⌊ m 2 ⌋ n − i ⌊ m + 1 2 ⌋ i − c

      c=0min(k,n)i=cn(ni)m2nim+12iS2(k,c)(ic)c!=c=0min(k,n)S2(k,c)(nc)c!i=cn(ncic)m2nim+12i=c=0min(k,n)S2(k,c)(nc)c!m+12ci=cn(ncic)m2nim+12ic" role="presentation" style="position: relative;">c=0min(k,n)i=cn(ni)m2nim+12iS2(k,c)(ic)c!=c=0min(k,n)S2(k,c)(nc)c!i=cn(ncic)m2nim+12i=c=0min(k,n)S2(k,c)(nc)c!m+12ci=cn(ncic)m2nim+12ic
      ==c=0min(k,n)i=cn(in)2mni2m+1iS2(k,c)(ci)c!c=0min(k,n)S2(k,c)(cn)c!i=cn(icnc)2mni2m+1ic=0min(k,n)S2(k,c)(cn)c!2m+1ci=cn(icnc)2mni2m+1ic

    • 接下来单独处理 ∑ i = c n ( n − c i − c ) ⌊ m 2 ⌋ n − i ⌊ m + 1 2 ⌋ i − c \sum_{i=c}^{n} \binom{n-c}{i-c} \lfloor \cfrac{m}{2}\rfloor^{n-i} \lfloor \cfrac{m+1}{2}\rfloor^{i-c} i=cn(icnc)2mni2m+1ic

      • 使用 i = i − c i=i-c i=ic换元,求和范围从 [ c , n ] [c,n] [c,n]变为 [ 0 , n − c ] [0,n-c] [0,nc]

      • 得到 ∑ i = 0 n − c ( n − c i ) ⌊ m 2 ⌋ n − c − i ⌊ m + 1 2 ⌋ i \sum_{i=0}^{n-c} \binom{n-c}{i} \lfloor \cfrac{m}{2}\rfloor^{n-c-i} \lfloor \cfrac{m+1}{2}\rfloor^{i} i=0nc(inc)2mnci2m+1i

      • 发现刚好为一个二项式展开的式子,使用二项式定理反推回去得到
        ∑ i = 0 n − c ( n − c i ) ⌊ m 2 ⌋ n − c − i ⌊ m + 1 2 ⌋ i = ( ⌊ m 2 ⌋ + ⌊ m + 1 2 ⌋ ) n − c = m n − c

        i=0nc(nci)m2ncim+12i=(m2+m+12)nc=mnc" role="presentation" style="position: relative;">i=0nc(nci)m2ncim+12i=(m2+m+12)nc=mnc
        i=0nc(inc)2mnci2m+1i=(⌊2m+2m+1)nc=mnc

    • 故原式等于

      ∑ c = 0 min ⁡ ( k , n ) S 2 ( k , c ) ( n c ) c ! ⌊ m + 1 2 ⌋ c ∑ i = c n ( n − c i − c ) ⌊ m 2 ⌋ c ( m + 1 2 ) i − c = ∑ c = 0 min ⁡ ( k , n ) S 2 ( k , c ) ( n c ) c ! ⌊ m + 1 2 ⌋ c m n − c

      c=0min(k,n)S2(k,c)(nc)c!m+12ci=cn(ncic)m2c(m+12)ic=c=0min(k,n)S2(k,c)(nc)c!m+12cmnc" role="presentation" style="position: relative;">c=0min(k,n)S2(k,c)(nc)c!m+12ci=cn(ncic)m2c(m+12)ic=c=0min(k,n)S2(k,c)(nc)c!m+12cmnc
      =c=0min(k,n)S2(k,c)(cn)c!2m+1ci=cn(icnc)2mc(2m+1)icc=0min(k,n)S2(k,c)(cn)c!2m+1cmnc

    写代码时,可使用 ( n c ) c ! = n ! ( n − c ) ! \binom{n}{c}c!=\cfrac{n!}{(n-c)!} (cn)c!=(nc)!n!

    • 然后我们便可以在 O ( m i n ( k , n ) ) = O ( 能过 ) O(min(k,n))=O(能过) O(min(k,n))=O(能过)的时间没计算出这个式子的值
    //其中Z表示模数类,运算时自动取余998244353,可参照jiangly代码
    
    #define rep(i, a, b) for (int i = (a); i <= (b); i++)
    Z S[K][K];
    void init()
    {//预处理斯特林数
        S[0][0] = 1;
        rep(i, 1, 2000)
            rep(j, 1, 2000)
                S[i][j] = S[i - 1][j - 1] + S[i - 1][j] * Z(j);
    }
    
    int main()
    {
        init();
        cin >> T;
        while (T--)
        {
            int n, m, k;
            cin >> n >> m >> k;
    
            //用于计算 [(m + 1) / 2] ^ c
            Z t = Z((m + 1) / 2); // 奇数的个数
            Z tpow = 1;
    
            //用于计算 组合数 * c!,即 n! / (n - c)! 
            Z C = 1;
    
            //用于计算 m ^ (n - c)
            Z p = power(Z(m), n);
            Z invm = Z(1) / Z(m);
    
            Z ans = 0;
            rep(c, 1, k)
            {
                if(c > n)
                    break;
                tpow *= t; // [(m + 1) / 2] ^ c
                C *= Z(n + 1 - c); // 组合数 * c!,即 n! / (n - c)! 
                p *= invm; // m ^ (n - c)
                Z tmp = S[k][c] * C * tpow * p;
                ans += tmp;
            }
            cout << ans << el;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
  • 相关阅读:
    Replication(下):事务,一致性与共识
    lab3_系统调用(下)
    项目架构落地之需求分析(一)
    灰色预测 Python
    腾讯云国际版云服务器欠费说明
    使用Nacos实现分布式配置管理和服务发现
    spring的annotation-driven配置事务管理器详解
    【闲言碎语】学习 文本编辑器vim及其插件、ranger、C语言、WSL配置、X11等等
    基于YOLOv8的车辆跟踪与车速计算应用
    MYSQL入门与进阶(十)
  • 原文地址:https://blog.csdn.net/Gh0st_Lx/article/details/126182889