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;
}
还是菜。