计算其乘积不能被k整除的子阵列的数量

给定一个数组,我想计运算符数据(连续)的数量,其中的产品在被采用时不会被k整除.

例如.设A = [1,2,3,4,5,6]和K = 2

那么产品不能被K整除的子阵列数是2:

{1}
{3}
{5}

其余的都可以被2整除.

{1, 2}
{1, 2, 3}
{1, 2, 3, 4}
{1, 2, 3, 4, 5}
{1, 2, 3, 4, 5, 6}
{2}
{2, 3}
{2, 3, 4}
{2, 3, 4, 5} etc..

我尝试首先计运算符阵列的总数(n)(n 1)/ 2,然后通过使用mod减去可被k整除的子阵列的数量,但它不起作用.我如何解决这个问题?

这会计算(错误地)产品可被K整除的子阵列数量:

int curr = 1;
    int x = 0;
    for (int i = 0; i < a.length; i++) {
        curr = curr * a[i] % k;
        if (curr == 0) {
            curr = 1;
            if (x > 0)
                ans = ans.subtract(NumberUtils.nChooseR(x + 1, 2));
            if (a[i] % k != 0) {
                x = 1;
            } else {
                x = 0;
            }
        } else {
            x++;
        }
    }
    if (x != 0) {
        ans = ans.subtract(NumberUtils.nChooseR(x + 1, 2));
    }
    return ans;

一个稍微相关的问题是this one,但它涉及添加,所以它不能在这里应用.

编辑:对数组大小的约束为10 ^ 5,对数组元素的约束为10 ^ 9.因此,我正在寻找线性或线性时间的解决方案

最佳答案 “大局”:

>很容易看出,如果[l,r]子阵列产品可以被K整除,那么[l,r 1]和[l – 1,r]的乘积也是如此.
>因此,如果我们能够有效地计运算符模K的乘积的值,我们就可以移动两个指针(左指针正好向右移动一个,而右指针一直在增加,直到乘积等于零模K为止) .这样,我们将获得从左指针位置开始并且其乘积可被K整除的子阵列的数量.答案将是左指针的所有值的总和.
>问题:我们无法明确存储产品(它太大了).我们不能做模运算,因为移动左指针需要模除法.我们可能没有逆.

解决方案1:

让我们使用一个分段树来计算O(log N)时间内任意子阵列的乘积K(我们只需构建树并在每个节点中存储相应范围模K的乘积.现在我们只需要将这些值相乘(模K)用于查询范围被分解成的所有节点.我们可以使用两个指针,因为我们可以有效地计算任何子模K的乘积.

解决方案2:

分而治之.

让我们将数组分成两个(几乎)相等的一半,并递归地解决每个数组的问题.现在我们只需要计算从上半部分开始到第二部分结束的良好子数组的数量.但是每个这样的子阵列的乘积是[1,m]和[m 1,r]部分的乘积(所有计算均以K为模).但是m是固定的(它是分割数组的位置).因此,我们可以预先计算所有l的[l,m]乘积和线性时间内所有r的[m 1,r]乘积.现在我们可以再次使用两个指针(它们分别用0和m 1初始化).我们可以在线性时间内“合并”两半,因此总时间复杂度也是O(N log N).

点赞