题解/算法 {3145. 大数组元素的乘积}

题解/算法 {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的幂(即xA[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;
}
  • 12
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值