逆元+尺取

题目描述
给出一个长度为 n 的数列 a_1,a_2,\ldots,a_na
1

,a
2

,…,a
n

,求其长度为 k 的连续子段的乘积对 998244353 取模余数的最大值。
输入描述:
第一行两个整数n,k。
第二行n个整数,a_1,a_2,\ldots,a_na
1

,a
2

,…,a
n


输出描述:
输出一个整数,代表最大余数。
示例1
输入
复制
5 3
1 2 3 0 8
输出
复制
6
说明
123\mod 998244353=61∗2∗3mod998244353=6
备注:
1 \le k \le n \le 2*10^51≤k≤n≤2∗10
5

0 \le a_i <9982443530≤a
i

<998244353

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod = 998244353;
const ll N  = 200005;
ll a[N];
ll Max = 0;//记录最终结果
ll sum = 1;
ll Qpow(ll x,ll k)//快速幂求逆元
{
    ll ans = 1;
    while(k)
    {
        if(k%2!=0) ans = ans*x%mod;
        k=k>>1;
        x=x*x%mod;
    }
    return ans;
}
int main()
{
    ll n,k;
    cin>>n>>k;
    for(int i=1;i<=n;i++)
    {
       cin>>a[i];   
    }
    ll l = 1; //尺取法
    ll r = 1;
    while(r<=n)
    {
        if(a[r]) //不是0就累乘直到达到k段子段
        {
            sum=(sum*a[r])%mod;//sum是每一次扫描成功的值
            if((r-l+1)%k==0) //达到k子段
            {
                if(sum>Max) Max = sum;//题上要求取最大的
                sum = sum*Qpow(a[l],mod-2)%mod;//逆元 ,原本是sum=sum/a[l] 。
                //一次扫描成功后,就吧最左端的元素除掉,再继续扫描。当然不能直接除 ,
                //这里需要用到逆元知识,不懂的可以去了解下 ,反正我那个式子就
                /*是逆元=a[l]的mod-2次幂;*/
      
                l++;
            }
 
        }
        else  //再未达到k个非零元素的子段前,如果遇到0,当前的区间就废了 
        {
             l = r + 1;// 左端点直接到0的下一个位置
             sum = 1;//sum归1
        }
        r++;
    }
    cout<<Max<<endl;
    return 0;
}

逆元:1.什么是逆元

当求解公式:(a/b)%m 时,因b可能会过大,会出现爆精度的情况,所以需变除法为乘法:
模p意义下,一个数b如果有逆元x,那么除以b相当于乘以x。;
逆元就是这样应用的;若x满足a*x=1(mod p);
我们称x是a在modp意义下的逆元

2.逆元的快速幂求法(求a*c(mod m));

费马小定理:费马小定理(Fermat’s little theorem)是数论中的一个重要定理,在1636年提出。如果p是一个质数,而整数a不是p的倍数,则有a^(p-1)≡1(mod p) a * a^(p-2)=1(mod p);
所以用快速幂求出a^(p-2)即为逆元,(a/b)%m用a*b的逆元再除模即可;
求逆元用快速幂代码

#include<cstdio>
  #define LL long long 
  using namespace std;
  const LL MAXN=200000001;
  LL n,mod;
  LL fastpow(LL val,LL p)
  {
     LL base=1;
     while(p)
    {
        if(p&1)    base=(base*val)%mod;
         val=(val*val)%mod;
         p>>=1;
    }
     return base;
 }
 int main()
 {
     scanf("%lld%lld",&n,&mod);
     for(LL i=1;i<=n;i++)
         printf("%lld\n",fastpow(i,mod-2)%mod);
     return 0;
 }
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值