2020牛客寒假算法基础集训营4 C 子段乘积

时间:2022-07-26
本文章向大家介绍2020牛客寒假算法基础集训营4 C 子段乘积,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

给出一个长度为 n的数列

a1,a2,a3,a4,…,an

求其长度为 k 的连续子段的乘积对 998244353 取模余数的最大值。

数据范围:1<=k<=2e5,0<=ai<998244353

法一:由于它限制了连续子段所包含的元素数目,很明显只有n-k+1

段,枚举就好,但是枚举的时候应该是要用到乘法逆元,因为你要乘下一个数ai+1, 除上一个被你踢出的元素ai+1-k ,同时你得考虑到这个数是零的情况,需要用到乘法逆元

乘法逆元,一般用于求a/b%p的值(p 通常为质数),是解决模意义下a/b的有效方法手段,在这里比方说要求a/b%p,那么就是a*inv(b),inv(b)表示b的逆元(模p意义下),inv(b)*b%p=1。

求逆元这里用个简单方法 首先引入费马小定理,两个数a,p如果p是质数,gcd(a,p)=1即a不是p的倍数,那么有a^p-1%p=1 换句话说就是a的p-1次方与1同余于p,那么a的逆元不就是a^(p-2)吗?快速幂一次即可求出

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define rg register ll
#define mod 998244353
ll n,k,ans,a[200005];
inline ll qpow(ll a,ll b)
{
    ll res=1;
    while(b)
    {
        if(b&1)res=res*a%mod;
        a=a*a%mod;
        b>>=1;
    }
    return res;
}
int main()
{
    cin>>n>>k;
    ll cnt=0,res=1;
    for(rg i=1;i<=n;i++)cin>>a[i];
    for(rg i=1;i<=n;i++)
    {
        if(!a[i]) {cnt=0,res=1;}
        else
        {
            if(cnt<k)//1~k-1的时候执行这个语句
            {
                res=res*a[i]%mod;
                cnt++;
                if(cnt==k)ans=max(ans,res);
            }
            else//t一个进一个
            {
                res=res*qpow(a[i-k],mod-2)%mod;
                res=res*a[i]%mod;
                ans=max(ans,res);
            }
            
        }
        
    }
    cout<<ans<<endl;

    while(1)getchar();
    return 0;
}

法二:分治or线段树

线段树和分治一个思想,都是优雅的暴力,比方说我要求1~n的乘积我怎么求?我先求1~n/2的,再求n/2+1~n的,这样就能保证logn就可得到结果,那么我们无非最多进行n-k+1次查询,时间复杂度不过nlogn,可过!

自己写的线段树,可能常数有点大

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define rg register ll
#define mod 998244353
ll n,k,a[200005],sum[200005*4];
inline void push_up(ll rt)
{
    sum[rt]=(sum[2*rt]%mod)*(sum[2*rt+1]%mod)%mod;
}
inline void build(ll l,ll r,ll rt)
{
    if(l==r){sum[rt]=a[l];return ;}
    ll m=(l+r)>>1;
    build(l,m,rt<<1);
    build(m+1,r,rt<<1|1);
    push_up(rt);
}
inline ll query(ll l,ll r,ll rt,ll a,ll b)
{
    ll res=1;
    if(a<=l&&b>=r){return sum[rt];}
    ll m=(l+r)>>1;
    if(m>=a)  res=(res%mod)*(query(l,m,rt<<1,a,b)%mod)%mod;
    if(m+1<=b) res=(res%mod)*(query(m+1,r,rt<<1|1,a,b)%mod)%mod;
    return res;
}
int main()
{
    cin>>n>>k;
    for(rg i=1;i<=n;i++)cin>>a[i];
    build(1,n,1);
    ll ans=0;
    for(rg i=1;i+k-1<=n;i++)
    {
        ans=max(ans,query(1,n,1,i,i+k-1));
        
    }
    cout<<ans<<endl;
    while(1)getchar();
    return 0;
}