[CF1204E] Natasha, Sasha and the Prefix Sums

[CF1204E] Natasha, Sasha and the Prefix Sums

题意

一个长度为 \(n+m\),有 \(n\)\(1\)\(m\)\(-1\) 的序列 \(a\),定义其最大前缀和为:\(\large \max\{ 0,\max\limits_{1\le i\le n+m}\sum\limits_{j=1}^ia_j \}\)。求算所有可能序列的最大前缀和。

分析

首先我们考虑一种计算总贡献的方法,首先我们有一种朴素的想法,即我们枚举状态 \(s\) 时,求算 \(\sum f(s)\),但是这道题显然我们不能暴力枚举每种情况,我们考虑另外一种情况,若设某个序列的贡献为 \(val\) 时,我们可以使用 \(\sum cnt_{val}\times val\) 来求解。

对于这道题我们可以使用后者来求解,我们考虑前缀和区间以及题目中的定义,显然有 \(val∈[\max(1,n-m),n)\)。而后我们可以考虑使用类似 Catalan 数的方法进行求解,我们考虑把序列中的 \(1\) 看做向右走,序列中的 \(-1\) 看做向左走,则有最大前缀和为过程中坐标的最大值,则我们考虑一种 \(f(k)\) 表示不向右越过 \(k\) 的方案数,则有 \(f(k)\) 表示最大前缀和小于等于 \(k\) 的方案数,显然我们有最大前缀和的方案数为 \(f_k-f_{k-1}\)。而后我们考虑单个 Catalan 序列的求解。

我们不考虑限制条件那么所有的操作方案数为 \(C_{n+m}^m\)

而后我们考虑这个不越过 \(k\) 的限制条件。显然有所有不合法操作都会抵达 \(k+1\)。那么我们把所有这一时刻后的不合法操作以 \(k+1\) 为对称轴取反。则有最后我们经过一些操作,由原来的终点 \(n-m\) 变为 \(2(k+1)-n+m\)。我们设向右走 \(R\) 步且为正贡献 1,向左走 \(L\) 步且为负贡献 1。则有二元一次方程组 \(L+R=n+m,R-L=2(k+1)-n+m\)

那么我们解得 \(L=n-k-1,R=m+k+1\)。则有不合法路径数 \(C_{n+m}^{m+k+1}\)

则有 \(f(k)=C_{n+m}^{m}-C_{n+m}^{m+k+1}\)

我们考虑总贡献有 \(Ans=\sum_{k=\max(1,n-m)}^{n}k \times (f(k)-f(k-1))\)

我们考虑使用快速幂+逆元求算组合数。

代码

#include<bits/stdc++.h>
using namespace std;
#define int long long
int n,m;
const int mod=998244853;
struct node{
  int inv[2000090],fac[2000090];
  int qpow(int shu,int cifang){
    int ans=1;int k=cifang;
    while(k){
      if(k&1){ans=ans*shu%mod;ans%=mod;shu=shu*shu%mod;shu%=mod;}
      else{shu=shu*shu%mod;shu%=mod;}
      k>>=1;
    }
    return ans%mod;
  }
  void init(int len){
    fac[0]=1;
    for(int i=1;i<=len;i++) fac[i]=fac[i-1]*i%mod;
    inv[len]=qpow(fac[len],mod-2);
    for(int i=len;i;i--){
      inv[i-1]=inv[i]*(i)%mod;
    }
  }
  int C(int n,int m){
    return fac[n]%mod*inv[m]%mod*inv[n-m]%mod;
  }
}lg_get;

int f(int n,int m,int k){
  int ans=lg_get.C(n+m,m)%mod-lg_get.C(n+m,m+k+1)%mod;
  ans+=mod,ans%=mod;
  return ans;
}
void solve(){
  scanf("%lld%lld",&n,&m);
  // int x=k,y=a;
  // int y=k,x=a;
  // x--,y--;
  // printf("%lld %lld\n",x,y);
  int ans=0;
  for(int kk=max(1ll,n-m);kk<=n;kk++){
  	// printf("%d\n",kk);
    int tmp=kk%mod*(f(n,m,kk)-f(n,m,kk-1))%mod;
    tmp%=mod;
    // printf("%lld\n",tmp);
    ans+=tmp;
    ans+=mod;
    ans%=mod;
  }
  printf("%lld\n",ans);
}
const int MAXN=1e6+7;
signed main(){
  lg_get.init(MAXN*2);
  // int T;
  // cin>>T;
  // while(T--) solve();
  solve();
}
posted @ 2023-07-25 14:17  Zimo_666  阅读(7)  评论(0编辑  收藏  举报