LG8563

题目传送门

貌似各路神仙都用线段树、平衡树秒了这题,蒟蒻在此献上一篇暴力的题解。

分析

看到数据范围 $ 1 \le n,q \le 2 \times 10^5$,而且还要求区间最大乘积 \(M\),很容易想到用线段树。但当我们看完题目,看到当 \(M\) 大于 \(2^{30}\) 时直接输出一个字符串,便发现很多情况下是无解的。

在本题中,由于保证了所有的 \(a_i\)(包括修改后)满足 $ \left\vert a_i \right\vert \ge 2$,所以可以看出 \({a_i}^k\) 很容易便超过了 \(2^{30}\),因此 \(k\) 只是一个很小的常数。

因此我们只要确定一个合适的 \(k\) 就可以了。对于每次查询,首先判断区间长度是否大于 \(k\),若在合理的范围内则分两种情况:

  • 区间内负数的个数为偶数

这种情况十分简单,“负负得正”,将所有数乘起来即可。

  • 区间内负数的个数为奇数

这种情况下就选取最长的连续正整数区间即可。

当然,为了避免爆范围,应当边乘边判断,一旦超过即输出。

时间复杂度 \(O(n+qk)\)

Code

#include <iostream>
#include <cstdio>
#define MX 1<<30
#define int long long
using namespace std;
int n,m,t,l,r,a[1000001];
long long b[1000001],c[5001][5001];
signed main()
{
	cin>>n>>m;
	for(int i=1;i<=n;i++)
		cin>>a[i];
	for(int i=1;i<=m;i++)
	{
		cin>>t>>l>>r;
		if(t==1) a[l]=r;
		if(t==2)
		{
			if(r-l>60)
			{
			    printf("Too large\n");
			    continue;
			}
			int cnt=0;
			for(int j=l;j<=r;j++)
				if(a[j]<0)
					cnt++;
			if(cnt&1^1)
			{
				int pd=0,sum=1ll;
				for(int j=l;j<=r;j++)
				{
					sum*=a[j];
					if(abs(sum)>MX)
					{
						pd=1;
						break;
					}
				}
				sum=max(sum,1ll);
				if(pd) printf("Too large\n");
				else printf("%lld\n",sum);
			}
			else
			{
				int k1,k2,sum1=1ll,sum2=1ll,pd=0;
				for(int j=l;j<=r;j++)
					if(a[j]<0)
					{
						k1=j;
						break;
					}
				for(int j=r;j>=l;j--)
					if(a[j]<0)
					{
						k2=j;
						break;
					}
				for(int j=r;j>k1;j--)
				{
					sum1*=a[j];
					if(abs(sum1)>MX)
					{
						pd=1;
						break;
					}
				}
				for(int j=l;j<k2;j++)
				{
					sum2*=a[j];
					if(abs(sum2)>MX)
					{
						pd=1;
						break;
					}
				}
				sum1=max(sum1,1ll);
				sum2=max(sum2,1ll);
				if(pd) printf("Too large\n");
				else printf("%lld\n",max(sum1,sum2));
			}
		}
	}
	return 0;
}
posted @ 2024-01-20 17:46  liyilang2021  阅读(1)  评论(0编辑  收藏  举报