P8563 Magenta Potion
P8563 Magenta Potion
进入正题【doge】
本题一眼就是线段树,但是其实仔细一读,这道题因为存在“Too large”用暴力枚举即可。
怎样寻找乘积最大的子区间?
题目要求输出
首先,如果区间中的奇数的个数是偶数个(3 ,-2 , 9 , -4 , 3),因为负负得正
所以 奇数的个数是偶数个时 最大乘积一定是 所有数的乘积
如果, 如果区间中的奇数的个数是奇数个(-3 ,-2 , 9 , -4 , 3)
只需要删除一个奇数,乘积就会为 正数;
答案一定是把最左(右)边的负数除掉(本例中就是删除左边的(-3)或者右边的(-4 , 3))
我们只去要暴力枚举从
所以 奇数的个数是奇数个时 删除
为什么暴力就可以实现?
读题发现 因为
而
那么暴力就成为了可能。
我们举一种最极端的例子(对于所有的
但本题还会存在负数的情况 , 因为我们只能从两边删除数字 所以为了让结果减小对多, 我们让负数在最中间
如下表格:
为了让最终的结果最大,我们必定会删除一半的数
删除任意一半都会留下 剩下 的
*呢么我们就可以发现 实际我们 需要的时间复杂度是$O(60 n)$**
代码实现
本题有 2 种操作
对于 操作1 需要单点修改 因为我们用暴力求解所以只需要用数组存数,单点修改直接赋值即可
对于 操作2 求出区间乘积最大的子序列 前面我们已经证明过寻找的方法即:
奇数的个数是偶数个时 最大乘积一定是 所有数的乘积
奇数的个数是奇数个时 删除
先判断 会不会超过 范围
if(r - l > 63) {cout << "Too large\n"; continue;}
如果可能出现小于等于
__int128 fi_neg = 0 ,fi_sum = 1 , la_sum = 1, Sum = 1 , f = 0;
// fi_sum 为前半段乘积 la_sum 为后半段乘积 Sum 为 总乘积
// f 为 Sum 真实值 为 '正' 还是 '负'
// fi_neg 记录是否出现过第一个负数
for(int i = l;i <= r;++ i){
Sum *= a[i];
if(a[i] < 0) f ^= 1;
// f 初值为 1 , 1 ^ 1 = 0 , 0 ^ 1 = 1
// 这里实现了 用 f 记录是否为正数
if(!fi_neg && a[i] < 0) fi_neg = i , fi_sum *= a[i];
if(!fi_neg) fi_sum *= a[i];
if(a[i] < 0) la_sum = Sum / a[i];
}
__int128 ans = -1;
if(la_sum)la_sum = Sum / la_sum;
再按顺序判断
注: 本题开int128 也会焯范围,但是因为int128 比较大所以不会再次溢出成为正数。\
所以在答案一定为正的时候记录了负数说明溢出了(真实值>
if(Sum < 0 && !f){cout << "Too large\n"; continue;} // 防止溢出
if(Sum > (1ll << 30)){cout << "Too large\n"; continue;}
// 为正数且超范围
if(Sum > 0){write(Sum); cout << "\n";continue;}
// 为整数且不超范围
if(la_sum) ans = max(Sum / la_sum , ans);
if(fi_sum) ans = max(Sum / fi_sum , ans);
// 计算删除前(后)半段的最大值
if(ans > (1ll << 30)){cout << "Too large\n"; continue;}
// 超范围
write(max((__int128)1 , ans));cout << "\n";
// 不超范围
注:本代码用到了__int128,输出时要用 '快写’;因为本题没有输出负数的情况所以不需要管x的正负
inline void write(__int128 x){
if(x > 9)
write(x/10);//上一位
putchar(x % 10 + '0');//取出来这一位然后putchar
}
讲解结束【doge】上AC代码
#include<bits/stdc++.h>
using namespace std;
#define xztx main
#define ll long long
#define Mod
int n , q;
ll a[2000020];
inline void write(__int128 x){
if(x > 9)
write(x/10);
putchar(x % 10 + '0');
}
void Init(){
cin >> n >> q;
for(int i = 1;i <= n;++ i)
cin >> a[i];
}
int xztx(){
Init();
for(int k = 1;k <= q;++ k){
int op , l , r;
cin >> op >> l >> r;
if(op - 1){
if(r - l >= 63) {cout << "Too large\n"; continue;}
__int128 fi_neg = 0 ,fi_sum = 1 , la_sum = 1, Sum = 1 , f = 0;
for(int i = l;i <= r;++ i){
Sum *= a[i];
if(a[i] < 0) f ^= 1;
if(!fi_neg && a[i] < 0) fi_neg = i , fi_sum *= a[i];
if(!fi_neg) fi_sum *= a[i];
if(a[i] < 0) la_sum = Sum / a[i];
}
__int128 ans = -1;
if(la_sum)la_sum = Sum / la_sum;
if(Sum < 0 && !f){cout << "Too large\n"; continue;}
if(Sum > (1ll << 30)){cout << "Too large\n"; continue;}
if(Sum > 0){write(Sum); cout << "\n";continue;}
if(la_sum) ans = max(Sum / la_sum , ans);
if(fi_sum) ans = max(Sum / fi_sum , ans);
if(ans > (1ll << 30)){cout << "Too large\n"; continue;}
write(max((__int128)1 , ans));cout << "\n";
} else a[l] = r;
}
return 0;
}