zoukankan      html  css  js  c++  java
  • [2020多校A层11.25] 最大前缀和

    fzszkl有 (N)(1)(M)(-1) 。他将它们随意排成一列,记为序列 (A) ,并求出了这个序列的前缀和,找到了前缀和的最大值(注意,第 (0) 个位置的前缀和也算,所以这个最大值一定是非负整数)。

    因为fzszkl很无聊,找一个不过瘾,所以他想知道:对于所有本质不同的序列,前缀和的最大值之和是多少。

    对于一个序列 (A) ,它的前缀和的最大值 (F(A)) 的严谨定义为:

    [F(A)=max{0,max_{i=1}^{N+M}{sum_{j=1}^iA_j}} ]

    答案对 (998244853) (是个质数)取模。

    (N,Mle 2e3)


    早上考试遇到的题,当时没做出来,下午想出来之后感觉非常不错,所以记录下解法(而不是像某人上午对着原题题解抄一遍就会了)。

    发现 (max) 这个限制是不好处理的,那么我们可以枚举最大值,假设当前枚举到了最大值为 (x) ,那么问题转化为:

    • ((0,0)) 走到 ((n,m)) ,必须碰到 (y=x+b) 这条直线的方案数。

    因为 (1,-1) 类似于一个括号序列,而我们现在多了个存在一个前缀和 (ge x) 的要求。

    考虑翻转坐标系,看作从 (n+m) 次选数,数只有 (1,-1) ,最后和为 (n-m) ,那么可以把这个问题进一步转化为:

    • ((0,0)) 走到 ((n+m,n-m)) ,每次往右上或右下走一步,且必须穿过 (y=x) 这条线的方案数。

    然后这个问题就好做许多了,我们可以分情况讨论:

    1. ((0,0))(y=x) 下面,((n+m,n-m))(y=x) 上面

    那么不管怎么走一定会穿过 (y=x) ,那么我们设网上走了 (t) 步,可以列出如下方程:

    [t-(n+m-t)=n-m ]

    可以解得 (t=n) ,那么往上走了 (n) 步,往下走了 (m) 步,就是 ({n+m}choose n)

    1. ((0,0))(y=x) 下面,((n+m,n-m)) 也在 (y=x) 下面

    这样子就会有穿不过的情况了,于是我们考虑做 ((0,0)) 关于 (y=x) 的对称点 ((0,2x)) ,那么这个问题等价于从 ((0,2x)) 按原问题走法走到 ((n+m,n-m)) ,而这样子就保证一定能穿过了,所以我们继续列刚才那样的方程:

    [2t-(n+m)=2x-(n-m) ]

    可以解得 (t=x+m) ,那么往上走了 (x+m) 步,往下走了 (n+m-(x+m)) 步,就是 ({n+m}choose{x+m})

    Code

    #include <iostream>
    #include <cstdio>
    #include <algorithm>
    #include <cstring>
    const int N = 2e3;
    const int M = 1e5;
    const int p = 998244853;
    using namespace std;
    int n,m,inv[M + 5],fac[M + 5],ans,sm[M + 5],res;
    int C(int n,int m)
    {
        if (m > n || n < 0)
            return 0;
        return 1ll * fac[n] * inv[m] % p * inv[n - m] % p;
    }
    int main()
    {
        scanf("%d%d",&n,&m);
        fac[0] = 1;
        for (int i = 1;i <= M;i++)
            fac[i] = 1ll * fac[i - 1] * i % p;
        inv[1] = 1;
        for (int i = 2;i <= M;i++)
            inv[i] = 1ll * (p - p / i) * inv[p % i] % p;
        inv[0] = 1;
        for (int i = 1;i <= M;i++)
            inv[i] = 1ll * inv[i - 1] * inv[i] % p;
        for (int i = 1;i <= n;i++)
        {
            if (i <= n - m)
                ans += C(n + m,n),ans %= p;
            else
                ans += C(n + m,i + m),ans %= p;
        }
        cout<<ans<<endl;
        return 0;
    }
    
  • 相关阅读:
    Linux 日志管理
    Linux 备份与恢复
    Linux 启动管理
    Linux 用户和用户组管理
    产生指定时间区间序列、按指定单位变化时间 python实现
    python上数据存储 .h5格式或者h5py
    数据预处理之独热编码(One-Hot Encoding)
    残差网络
    GBDT为什么不能并行,XGBoost却可以
    百融金服、趣店、中航信面试总结
  • 原文地址:https://www.cnblogs.com/sdlang/p/14037166.html
Copyright © 2011-2022 走看看