ACM-ICPC 2018 焦作赛区网络预赛 Transport Ship

题意:有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\n",dp[n][x]);
50     }
51 }
52 
53 int main(){
54     int T;
55     scanf("%d",&T);
56     while (T--) work();
57     return 0;
58 }
View Code

猜你喜欢

转载自www.cnblogs.com/MyGirlfriends/p/9653231.html
今日推荐