《牛客竞赛 - 子段乘积》的题解
题目的描述
给出一个长度为 n 的数列 a1,a2,…an ,求其长度为 k 的连续子段的乘积对 998244353 取模余数的最大值。
Input
第一行两个整数n,k。
第二行n个整数,a1,a2,…,an
Output
输出一个整数,代表最大余数。
Sample Input
5 3
1 2 3 0 8
Sample Output
6
题目的备注
1 ≤ k ≤ n≤2∗1e5
0 ≤ ai <998244353
解题思路和方法
这一次解题我们需要用到逆元或者神奇的线段树。
呃...由于我不会逆元,就学了线段树hhhh.
一棵树的节点为x,那么它的左子树的位置为x*2,右子树的位置为x*2+1
那么下面就是完整代码啦
完整代码
#include <iostream>
#include <stdio.h>
#include <algorithm>
using namespace std;
typedef long long ll;
const ll N=2*1e5+2; //输入的最大长度
const ll mod=998244353; //取模用的
ll tree[4*N]; //为了实现树,大小乘上4
void woc(int x)
{
tree[x]=(tree[x*2]%mod)*(tree[x*2+1]%mod)%mod;//将左右子树乘上去
}
void bulid(ll l,ll r,ll x)
{
if(l==r)
{
scanf("%lld",&tree[x]);//到确定的某个点后输入
return;
}
ll mid=(l+r)/2;
bulid(l,mid,x*2); //创造左子树
bulid(mid+1,r,x*2+1); //创造右子树
woc(x); //进行不可描述的操作(见上面的函数)
}
ll gold(ll a,ll b,ll l,ll r,ll x)
{
if(a<=l&&b>=r) //如果这个区间被所求区间包含
{
return tree[x]; //那么直接返回!
}
ll ans=1,mid=(l+r)/2;
if(a<=mid) ans=ans*gold(a,b,l,mid,x*2)%mod; //分别求出在中点左边的部分
if(b>=mid+1) ans=ans*gold(a,b,mid+1,r,x*2+1)%mod; //以及右边的部分
return ans;
}
int main()
{
ll n,k;
scanf("%lld %lld",&n,&k);
bulid(1,n,1); //首先在1-n的区间内创建树!
ll i,j,ans=0;
for(i=k;i<=n;i++)
{
ans=max(ans,gold(i-k+1,i,1,n,1));
//这里是求区间[i-k+1,i]的乘积
//每一次比较大小,保留最大的
}
printf("%lld\n",ans);//然后输出就完事啦
return 0;
}