题目描述
给出一个长度为 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;
}