题意:有N种船,每种船载重Vi,有2^Ci-1艘,然后有q个询问,每个询问输入一个s,输出有多少种不同的方案使得总载重确切为s。
题解:dp(i,j)表示前i种船,总载重为j的方案数。那么,显然dp(i,j)=sigma dp(i-1,j-k*Vi) (k<2^Ci && j-k*Vi>=0)。初始dp(0,0)=1。这样复杂度是O(NS^2),考虑计算前缀和实现O(1)转移。可以发现j-k*Vi与j同余,那么就按照不同余数分类,这样就可以通过前缀和来转移了。我用的是类似于滑动窗口的做法,保证区间中有小于2^Ci个数,同时维护区间和,这样复杂度就变成O(NS)了。
代码:
1 #include <iostream> 2 #include <cstdio> 3 #include <algorithm> 4 #include <cmath> 5 #include <vector> 6 #include <queue> 7 #include <set> 8 #include <map> 9 #include <string> 10 #include <string.h> 11 #include <stdlib.h> 12 #include <time.h> 13 #include <climits> 14 15 using namespace std; 16 17 const int mod=1e9+7; 18 19 long long dp[21][10000+7]; 20 int v[21],c[21]; 21 22 void work(){ 23 int n,q; 24 scanf("%d%d",&n,&q); 25 26 for (int i=1;i<=n;i++) scanf("%d%d",v+i,c+i); 27 28 int N=10000; 29 memset(dp,0,sizeof(dp)); 30 dp[0][0]=1; 31 32 long long s; 33 for (int i=1;i<=n;i++) { 34 for (int k=0;k<v[i];k++) { 35 s=0; 36 for (int j=k;j<=N;j+=v[i]) { 37 s+=dp[i-1][j]; 38 if (j>=(1<<c[i])*v[i]) s-=dp[i-1][j-v[i]*(1<<c[i])]; 39 s=(s+mod)%mod; 40 41 dp[i][j]=s; 42 } 43 } 44 } 45 46 while (q--) { 47 int x; 48 scanf("%d",&x); 49 printf("%lld ",dp[n][x]); 50 } 51 } 52 53 int main(){ 54 int T; 55 scanf("%d",&T); 56 while (T--) work(); 57 return 0; 58 }