ZOJ - 3216:Compositions (DP&矩阵乘法&快速幂)
We consider problems concerning the number of ways in which a number can be written as a sum. If the order of the terms in the sum is taken into account the sum is called a composition and the number of compositions of n is denoted by c(n). Thus, the compositions of 3 are
- 3 = 3
- 3 = 1 + 2
- 3 = 2 + 1
- 3 = 1 + 1 + 1
So that c(3) = 4.
Suppose we denote by c(n, k) the number of compositions of n
with all summands at least k. Thus, the compositions of 3 with all
summands at least 2 are
- 3 = 3
The other three compositions of 3 all have summand 1, which is less than 2. So that c(3, 2) = 1.
Input
The first line of the input is an integer t (t <= 30), indicate the number of cases.
For each case, there is one line consisting of two integers n k (1 <= n <= 109, 1 <= k <= 30).
Output
Output c(n, k) modulo 109 + 7.
Sample Input
2 3 1 3 2
Sample Output
4 1
题意:给定N,K,问N可以由多少个不小于K的数组合起来。
思路:当K=1时,就是隔板法,组合数之和,答案是2^(N-1) ;当K>1;可以得到方程dp[i]=dp[i-1]+dp[i-k];
我们用dpi表示和为i有多少种方案,那么考虑最后一个数,如果最后一个数=k,那么其方案数=dp[i-k];如果>k,那么其方案数=dp[i-1]; 想到这里就知道用矩阵乘法来做了。
#include<bits/stdc++.h> #define rep(i,a,b) for(int i=a;i<=b;i++) using namespace std; const int Mod=1e9+7; int L; int qpow(int a,int x) { int res=1; while(x){ if(x&1) res=1LL*res*a%Mod; a=1LL*a*a%Mod; x>>=1; } return res; } struct mat { int mp[31][31]; mat(){memset(mp,0,sizeof(mp));} mat friend operator*(mat a,mat b){ mat res; rep(k,1,L) rep(i,1,L) rep(j,1,L) (res.mp[i][j]+=1LL*a.mp[i][k]*b.mp[k][j]%Mod)%=Mod; return res; } mat friend operator^(mat a,int x){ mat res; rep(i,1,L) res.mp[i][i]=1; while(x){ if(x&1) res=res*a; a=a*a; x>>=1; } return res; } }; int main() { int T,N,K; scanf("%d",&T); while(T--){ scanf("%d%d",&N,&K); if(N<K) {puts("0"); continue;} if(K==1){ printf("%d\n",qpow(2,N-1)); continue;} mat ans,base; L=K; ans.mp[1][1]=1; base.mp[1][1]=base.mp[1][K]=1; rep(i,2,K) base.mp[i][i-1]=1; ans=(base^(N-K))*ans; printf("%d\n",ans.mp[1][1]); } return 0; }