我给了一些数字{a,b,c,d},我可以多次使用这些数字来使总和为'N'。我必须计算所有可能的组合,假设它为“ A”,并且我必须返回A%M,其中M是一些大质数。 对我造成问题的约束是N <= 10 ^ 18。集{a,b,c,d ..}的大小小于等于100。
我正在使用动态编程来解决它,但问题是我无法使用10 ^ 18大小的数组,如果我不缓存预先计算的值,则会超出时间限制。
#define M 1000000007
long long solve(long long N, vector<long long>& v, vector<long long>& dp){
if(N==0){
return 1;
}
if(dp[N]!=-1){
return dp[N];
}
int n = v.size(); // maximum size of v <=100
long long ans = 0;
for(int i=0;i<n;i++){
if(N-v[i]>=0){
ans = (ans + solve(N-v[i],v,dp))%M;
}
}
dp[N] = ans;
return ans;
}
int main(){
long long n, N; // n: size of set , N: expected sum
cin>>n>>N;
vector<long long>v(n);
for(int i=0;i<n;i++){
cin>>v[i];
}
long long ans = 0;
vector<long long>dp(N+1,-1);
for(int i = 0;i<n;i++){
if(N-v[i]>=0){
ans = (ans + solve(N-v[i],v,dp))%M;
}
}
cout<<ans<<endl;
}
如何对其进行优化以处理10 ^ 18的总和而不会浪费时间。
答案 0 :(得分:2)
为了举例说明,我假设您的集合是{1, 1, 3}
,并且我们要计算第100
项。但是这种方法通常会起作用。
让k
是您设置的最大值。让s[i]
为使i
求和的方式数量。我们的起始条件为s[-k+1] = 0
,s[-k+2]= 0
,...,s[-1] = 0
,但s[0] = 1
。我们对{a, b, c, ...}
的归纳步骤是s[n] = s[n-a] + s[n-b] + s[n-c] + ...
。
但是现在重新构想它。考虑向量v[n] = (s[n-k+1], s[n-k+2], ... + s[n])
。假设A[m]
是要乘以向量v[n+m] = (s[n+m-k+1], s[n+m-k+2], ..., s[n+m])
所必须乘以的矩阵。我假设它存在。如果您想算出斐波那契数列的通常证明,就可以推广。
现在,我们需要有关这些矩阵的两个事实。
第一个是A[m1 + m2] = A[m1] * A[m2]
。要看到这一点,请注意,对于所有n
,(A[m1] * A[m2])(v[n]) = A[m1]( A[m2]( v[n] ) ) = A[m1]( v[n + m2] ) = v[n + m2 + m1] = A[m1 + m2]( v[n] )
。
第二个是我们可以很容易地计算A[1]
。它在上对角线上全为1,然后在最后一行包含+1的行都出现在我们的集合中。 (或者,如果元素在我们的集合中两次,则为+2,因为在我们的示例中我确定是正确的。)因此对于我们的示例,我们有:
[0 1 0] [ v[n-2] ] [ v[n-1] ]
[0 0 1] * [ v[n-1] ] = [ v[n] ]
[1 0 2] [ v[n] ] [ v[n+1] ]
初始矩阵为A[1]
。
现在假设我们要计算s[100]
。那是v[100]
的最后一个条目。但是现在,我们将其减半,如下所示:A[100] = A[50] * A[50]
。 A[50] = A[25] * A[25]
。 A[25] = A[12] * A[13]
。 A[13] = A[1] * A[12]
。 A[12] = A[6] * A[6]
。 A[6] = A[3] * A[3]
。 A[3] = A[2] * A[1]
。最后,A[2] = A[1] * A[1]
。但是我们有A[1]
,这在8个矩阵相乘后得到A[100]
。由于所有内容都呈指数增长,因此理论上的答案有非常大的整数,但是这些操作很容易进行mod p
。
这可行吗?如果n = 10**18
最多有60个减半,那么用这种幼稚的方法,每个减半都可以对120个矩阵运算进行另一个+1乘法。如果集合中的最大元素为100,则表示每个矩阵乘法(半乘法,半加法)大约为200万,用于2.4亿次运算。
您还有很多工作要做。但是使用这种矩阵方法,至少是可行的。