链接:https://ac.nowcoder.com/acm/contest/3005/C
来源:牛客网
题目描述
给出一个长度为 n 的数列 a1,a2,…,an,求其长度为 k 的连续子段的乘积对 998244353 取模余数的最大值。
输入描述:
第一行两个整数n,k。
第二行n个整数,a1,a2,…,an。
输出描述:
输出一个整数,代表最大余数。
输入
5 3 1 2 3 0 8
输出
6
说明
1∗2∗3 mod 998244353=6
备注:
1≤k≤n≤2∗105
0≤ai<998244353
本题可以使用线段树,也可以分治,还可以使用尺取法,维护当前区间中有几个0,同时维护不是0的数字的乘积,这种方法需要使用乘法逆元。
思路:
尺取法,l代表左端点,r代表右端点。
l先不动,r往前扫描,如果成功扫到,有k个非0元素的子段就累成起来,最后把最左端的元素除了(用逆元),左端点往前移动,l++,再继续扫描。
再未达到k个非零元素的子段前,如果遇到0,当前的区间重置 ,左端点直接到0的下一个位置继续扫描。
1 #include <stdio.h> 2 #include <string.h> 3 #include <iostream> 4 #include <string> 5 #include <math.h> 6 #include <algorithm> 7 #include <vector> 8 #include <stack> 9 #include <queue> 10 #include <set> 11 #include <map> 12 #include <sstream> 13 const int INF=0x3f3f3f3f; 14 typedef long long LL; 15 //const int mod=1e9+7; 16 const int mod=998244353; 17 const int maxn=2e5+10; 18 using namespace std; 19 20 long long fpow(long long a, long long b) 21 { 22 if (a == 0) return 0; 23 long long ans = 1; 24 for (; b; b >>= 1, a = (a % mod * a% mod) % mod) 25 if (b & 1) ans = (ans % mod * a % mod) % mod; 26 return ans; 27 } 28 LL inv(LL a,LL mo)//用费马小定理求解a模mo的逆元 29 { 30 return fpow(a,mo-2)%mo; 31 } 32 33 LL a[maxn]; 34 35 int main() 36 { 37 #ifdef DEBUG 38 freopen("sample.txt","r",stdin); 39 #endif 40 41 int n,k; 42 scanf("%d %d",&n,&k); 43 LL ans=0; 44 for(int i=1;i<=n;i++) 45 scanf("%d",&a[i]); 46 LL sum=1; 47 int l=1;//尺取法 48 int r=1; 49 while(r<=n) 50 { 51 if(a[r]) 52 { 53 sum=sum*a[r]%mod; 54 if(r-l+1==k)//达到k子段 55 { 56 ans=max(ans,sum);//更新最大值 57 sum=sum*inv(a[l],mod)%mod;//除掉左端点(乘逆元) 58 l++; 59 } 60 } 61 else//如果遇到0,重置 62 { 63 l=r+1;// 左端点直接到0的下一个位置 64 sum=1;//sum重置为1 65 } 66 r++; 67 } 68 printf("%lld ",ans); 69 70 return 0; 71 }
-