子段乘积(尺取、逆元)

 

链接: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
0ai<998244353

 

本题可以使用线段树,也可以分治,还可以使用尺取法,维护当前区间中有几个0,同时维护不是0的数字的乘积,这种方法需要使用乘法逆元。

 

思路:

尺取法,l代表左端点,r代表右端点。

l先不动,r往前扫描,如果成功扫到,有k个非0元素的子段就累成起来,最后把最左端的元素除了(用逆元),左端点往前移动,l++,再继续扫描。

再未达到k个非零元素的子段前,如果遇到0,当前的区间重置 ,左端点直接到0的下一个位置继续扫描。

 

 1 #include 
 2 #include <string.h>
 3 #include 
 4 #include <string>
 5 #include 
 6 #include 
 7 #include 
 8 #include 
 9 #include 
10 #include <set>
11 #include 
12 #include 
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\n",ans);
69     
70     return 0;
71 }

 

 

 

 

-

你可能感兴趣的:(子段乘积(尺取、逆元))