K子集求和查询

时间:2014-08-14 19:57:39

标签: c++ algorithm

给出N个整数的列表A,我们被问到Q查询。每个查询由整数K表示,为此我们需要返回具有恰好K个元素的所有可能子列表的乘积之和。

我们需要以模块100003打印答案。

示例:设N = 3,数组为{1,2,3},并且有2个查询。

查询1:K = 1然后答案是6,因为对于K = 1,可能的子列表是{1},{2},{3}所以答案是1 + 2 + 3 = 6.

查询2:K = 2然后答案是11,因为对于K = 2,可能的子列表是{1,2},{2,3},{3,1}所以答案是(1×2)+(2× 3)+(3×1)= 2 + 6 + 3 = 11。

我的尝试:

#define MOD 100003
unsigned long long ans=0;
unsigned long long mulmod(unsigned long long a,unsigned long long b,unsigned long long c){
unsigned long long x = 0,y=a%c;
while(b > 0){
    if(b%2 == 1){
        x = (x+y)%c;
    }
    y = (y*2)%c;
    b /= 2;
}
return x%c;
}

void KSubset(unsigned long long *a,unsigned long long n,unsigned long long *s,unsigned long long sindex,unsigned long long index,unsigned long long k){

if (index>n)
    return;
if (k==0){
    unsigned long long productt = 1;
    for(int i=0;i<sindex;i++){
        productt=mulmod(productt,s[i],MOD);
    }
    ans=(ans%MOD + productt%MOD)%MOD;
    return ;
    }
s[sindex]=a[index];
KSubset(a,n,s,sindex+1,index+1,k-1);
KSubset(a,n,s,sindex,index+1,k);
}

但是由于查询可以达到N和N可以达到3 * 10 ^ 4.那么有没有更好的方法解决这个问题?

4 个答案:

答案 0 :(得分:2)

由于{3,1}不是子列表,我将假设您的意思是子集。所有计算都要完成mod 100003;没有问题,因为这里的一切都是代数的。如果展开因式多项式

(1 + 1 x) (1 + 2 x) (1 + 3 x)

其中每个因子对应一个输入值,那么你得到

1 + 6 x + 11 x^2 + 6 x^3,

并且查询q的答案是x ^ q的系数。朴素算法相当于扩展一个广义FOIL method,它需要指数时间。具有二次运行时间的更好的算法是逐个累积因子,具有类似

的循环
for (int i = degree; i > -1; i--) coefficient[i + 1] += coefficient[i] * x;

其中x是下一个值。

答案 1 :(得分:0)

如果您有许多查询,那么您可以为数组生成所有子集,
然后根据查询中的k,您可以选择(某些子集)。

ex:a = {1, 2, 3}

所有子集:{1}, {2}, {3}, {1, 2}, {2, 3}, {1, 3}, {}

当您使用k = 1查询(对所有大小为1的子集求和)时,
k = 2(在大小为2的子集上应用您的逻辑)。

但如果每个阵列只有1个查询,那么另一种算法可能会更好。

答案 2 :(得分:0)

这是一个O(n * log ^ 2 n)解决方案:

1)将其写为多项式(1 + x * a [0])*(1 + x * a [1])* ... *(1 + x * a [n - 1])。现在你需要扩展它以获得每个x度的系数。

2)你可以使用分而治之的方法来计算前半部分和后半部分(递归地)的乘积,而不是天真地乘以它们。现在,您可以使用傅立叶快速变换器快速乘以这两个获得的多项式。

3)T(n)= 2 * T(n / 2)+ n * log n = O(n * log ^ 2 n)。

答案 3 :(得分:-1)

使用STL,您可以:

std::size_t compute(const std::vector<int>& v, std::size_t k)
{
    std::vector<int> flags(v.size() - k, 0);
    flags.resize(v.size(), 1); // result in {0, .., 0, 1, .., 1} with k numbers 1

    std::size_t sum = 0;
    do {
        std::size_t mul = 1;
        for (std::size_t i = 0; i != v.size(); ++i) {
            if (flags[i]) {
                mul *= v[i];
                mul %= 10003;
            }
        }
        sum += mul;
        sum %= 10003;
    } while (std::next_permutation(flags.begin(), flags.end()));
    return sum;
}

Live example