题解/算法 {3145. 大数组元素的乘积}
@LINK: https://leetcode.cn/problems/find-products-of-elements-of-big-array/
;
要计算A[l...r]
这些数的乘积, 他等价于 1^? * 2^? * 4^? * 8^? * 16^? * ... (% Mod)
, 我们令Cont(x)
为x
的幂(即x
在A[l...r]
里出现的次数), 即答案是x^Cont(x) 的乘积
;
于是问题转换成: A[0,1,2,...,r]
里 有多少个x(是个二次幂)
, 这个问题直接计算是很困难的, 因为这个数组: A = [1, 10, 1,10, 100, 1,100, 10,100, ...]
, 是没啥规律的… 千万不要专注于这个数组, 而要是转换成B = [1, 2, 3, 4, 5, ...]
这个数组 (因为上面这个数组 本质就是根据1,2,3,4...
得到的)
.
我们专门有关于二进制的算法___Binary_CountOfBit
, 他可以计算: [0,1,2,...,R]
这些数里 二进制[bit] == 1
的数的个数, 还可以计算 [0,1,2,...R]
这些数的BitsCount
之和;
Prefix( r, x)
表示: A[0...r]
里面 有多少个x
; (注意 任意一个整数t
他最多有1个x
因为t
的二进制 要么是1 要么是0);
关键是如何计算呢? 比如A[r]
他是num
里的二进制, 也就是A[0...r] 是根据[1, 2, 3, 4, ..., num]而得到的
; 我们可以直接得到[0,1,2,...,num]
这些数里面 二进制位含有x
的数的个数 记作C
;
但现在有个问题, 一个数num
他二进制有很多个1
即一个num
会对应多个A的下标r
; 比如num = [10110001]
, 也就是他对应到A[]
里面是[1, 10000, 100000, 10000000]
, 假如此时A[r] == 1; x==10000
, 那么你的C
里面 是包含了10000
的, 但实际上 我们要抛去他 (即最后答案是C - (0/1)
);
.
只要怎么做呢? 我们先得到[0,1,2,...,num]
这些数的bitCount
之和R
, 然后让他做减法(每次去掉num
的最高位1) 直到他等于r+1
(这最多只会进行60次 因为num
最多有60位);
怎么根据r
得到num
呢? 这是也是二分的算法模板___BinarySearch_FromPrefixSumToIndex
; 令A[x]: x里的二进制1个数
, 我们现在知道Presum[x]: A的前缀和
, 我们的目的是 根据r
获得满足Presum[num] >= r
的最小的num
;
template< class _T_> _T_ ___BinarySearch_FromPrefixSumToIndex( _T_ const _sum){
#define PREFIXsum_( _ind) (Int64_)___BINARY_BitCountSum<unsigned long long>( _ind)
ASSERTsentence_( "PREFIXsum_(?) >= 0");
_T_ l = 0, r = _sum;
while( l < r){
auto mid = (l+r)>>1;
if( PREFIXsum_( mid) >= _sum){ r = mid;}
else{ l = mid+1;}
}
ASSERTsystem_( PREFIXsum_( r) >= _sum);
#undef PREFIXsum_
return r;
}
Int64_ Prefix( Int64_ _rig, int _bit){ // `A[0...,rig]` the count of `1<<bit`
Int64_ num = ___BinarySearch_FromPrefixSumToIndex( 1 + _rig); // 注意这里是`rig+1`;
Int64_ ANS = ___BINARY_CountOfBit<unsigned long long>( num, _bit);
Int64_ cont = ___BINARY_BitCountSum<unsigned long long>( num);
while( cont > (_rig+1)){
-- cont;
auto b = ___Binary_HigBit_1(num);
if( b == _bit){ -- ANS; break;}
___Binary_SetBit( num, b, 0);
}
return ANS;
}
Int64_ Interval( Int64_ _lef, Int64_ _rig, int _bit){
Int64_ ANS = Prefix( _rig, _bit);
if( _lef != 0){ ANS -= Prefix( _lef-1, _bit);}
return ANS;
}
vector<int> findProductsOfElements(vector<vector<long long>>& Q) {
vector<int> ANS( Q.size());
using Mod_ = ___Modulo_< int, -1>;
FOR_( q, 0, Q.size()-1){
Mod_::Modulus = Q[q][2];
Mod_ ans = 1;
FOR_( bit, 0, 60){
ans *= Mod_(1LL<<bit).Power( Interval( Q[q][0], Q[q][1], bit));
}
ANS[q] = ans.Value;
}
return ANS;
}