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

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;
}

发布了78 篇原创文章 · 获赞 9 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/Fiveneves/article/details/104281707