牛客寒假训练营 4 C 子段乘积

题目要求

链接:https://ac.nowcoder.com/acm/contest/3005/C
来源:牛客网

题目描述
给出一个长度为 n 的数列 a1,a2,…,ana_1,a_2,\ldots,a_na1​,a2​,…,an​,求其长度为 k 的连续子段的乘积对 998244353 取模余数的最大值。
输入描述:

第一行两个整数n,k。
第二行n个整数,a1,a2,…,ana_1,a_2,\ldots,a_na1​,a2​,…,an​。

输出描述:

输出一个整数,代表最大余数。

示例1
输入
复制

5 3
1 2 3 0 8

输出
复制

6

说明

1∗2∗3mod  998244353=6123\mod 998244353=61∗2∗3mod998244353=6

备注:

1≤k≤n≤2∗1051 \le k \le n \le 2*10^51≤k≤n≤2∗105
0≤ai<9982443530 \le a_i <9982443530≤ai​<998244353

解题思路

在对数组遍历时,要遇到一种情况,就是子段的长度大于要求的k段时,要除于a[i-k],又因为对ss取过模,一个取过模的数不能直接除一个数必须取逆元,所以不能简单地相除,逆元可以用费马小定理得到,即a^(p-1)≡1(mod p) 又因为a^(p-2) = (a^(p-1))* a(-1)则a(p-2)≡ a^(-1)(mod p),代码如下:

#include 
#include
using namespace std;
#define ll long long
const ll maxn=2e5+7;
const ll mod=998244353;
ll a[maxn];
ll n,k;
ll fema(ll m,ll n){//用快速幂来求m^n
          ll sum1=1;
          while(n>0){
               if(n&1) sum1=(sum1*m)%mod;
               m=(m*m)%mod;
               n>>=1;
          }
          return sum1;
}

int main()
{
    scanf("%lld%lld",&n,&k);
    ll i,sum,ss;
    i=1;
    sum=0;ss=1;//所求结果和当前段的乘积
    for(int ii=1;ii<=n;ii++)
               scanf("%lld",&a[ii]);
    int nu=0;//当前段0的个数
    for(int i=1;i<=n;i++){
               if(a[i]==0)nu++;
              else ss=(ss*a[i])%mod;
              if(i>=k+1){
              if(a[i-k]==0) nu--;//第前K个为0,不在当前段里,nu减掉,ss不用除
              else ss=(ss*fema(a[i-k],mod-2))%mod;//用费马小定理来表示s=s/a[i-k]%mod,因为s取过模,所以无法直接除其他数
              }
              if(nu==0&&i>=k)  //说明当前段长一定为K
              sum=max(sum,ss);
    }
    printf("%lld\n",sum);
    return 0;
}

你可能感兴趣的:(牛客寒假训练营 4 C 子段乘积)