A.子段乘积
题目链接-子段乘积
解题思路
- 维护当前区间中有几个0,同时维护不是0的数字的前缀积
- 因为长度为 k 的连续子段的乘积取模=(sum[i]/sum[i-k])%M
- 当求解公式:(a/b)%m 时,因b可能会过大,会出现爆精度的 情况,所以需变除法为乘法,即用到逆元:
关于费马小引理和逆元的解释
当p为质数时可以用快速幂求逆元
当p不是质数时,可以用扩展欧几里得算法求逆元
因为a有逆元的充要条件是a与p互质,所以gcd(a, p) = 1
由费马小定理得: b^(p-1)%p=1
则: b*b^(p-2)%p=1
两边同乘a/b,然后左右式交换得:a/b=a/b * b * b^(p-2)%p
化简得: a/b=a * b^(p-2)%p
所以(a/b)%p=a * (b^(p-2)%p)%p;
因为我实在太菜了,而且这道题p为质数,就不讲扩展欧几里得算法求逆元了,如果对逆元还不熟悉,可以去做一下 Acwing-快速幂求逆元板子题
附上代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int INF=0x3f3f3f3f;
const int N=2e5+5;
const int M=998244353;
typedef long long ll;
typedef pair<int,int> PII;
ll f(ll a,ll b){//快速幂
ll res=1;
while(b){
if(b&1)
res=res*a%M;
b>>=1;
a=a*a%M;
}
return res;
}
int a[N],sum[N],l[N];
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
int n,k;
cin>>n>>k;
sum[0]=1;
int cnt=0;
for(int i=1;i<=n;i++){
cin>>a[i];
if(a[i]==0){
sum[i]=1;
cnt++;
}
else{
sum[i]=(sum[i-1]*a[i])%M;
}
l[i]=cnt;
}
int maxn=0;
l[0]=0;
for(int i=k;i<=n;i++){
if(l[i]==l[i-k]&&sum[i]*f(sum[i-k],M-2)%M>maxn){
maxn=sum[i]*f(sum[i-k],M-2)%M;
}
}
cout<<maxn<<endl;
return 0;
}