补一个解题报告

上周周赛的解题报告……

是一个组合数学的DP

组合数学非常差劲却要担当DP任务的我对这类题真的很无奈。

比赛的时候没有思路。参考了一些DPS的思路,发现自己的差距真的是太大了

首先从问题的一般解法入手。

如果枚举取全部数据求和的话,从每位的角度看问题,就是要记录每一位上的数字乘以该位的权值。

所以DP的思想就是求除了某个数字的长为n的组合,设数字的总和为n 那么 数字i的和就是 长度为j(0 < j < n)的数字的组合数 * i * (11……11(j + 1个1))

于是要做的就是求出去某个数字后长度为j个数字的的组合方案数。

对于求一个数i的权值,如果他的个数大于0的话,那么减去一个(这个用于求权值,就是拜访的位置),用剩下的数用DP求一次组合就可以了。

代码如下

UVAlive 5063
   
     
1 #include < iostream >
2 #include < cstring >
3 #include < algorithm >
4 #include < cstdio >
5   using namespace std;
6   const int N = 10 ;
7 const int M = 100 ;
8 const long long MOD = 1000000007 ;
9
10 int a[N];
11 long long dp[N][M], f[M], c[M][M];
12
13 void preprocess()
14 {
15 f[ 0 ] = 1 ;
16 for ( int i = 1 ;i < M;i ++ )
17 f[i] = (f[i - 1 ] * 10LL) % MOD;
18 for ( int i = 0 ;i < M;i ++ )
19 {
20 c[i][ 0 ] = c[i][i] = 1 ;
21 for ( int j = 1 ;j < i;j ++ )
22 c[i][j] = (c[i - 1 ][j] + c[i - 1 ][j - 1 ]) % MOD;
23 }
24 }
25
26 long long DP()
27 {
28 memset(dp, 0 , sizeof (dp));
29 dp[ 0 ][ 0 ] = 1 ;
30 for ( int i = 1 ;i < N;i ++ )
31 for ( int j = 0 ;j < M;j ++ )
32 for ( int k = 0 ;k <= a[i] && dp[i - 1 ][j];k ++ )
33 dp[i][j + k] = (dp[i][j + k] + c[j + k][k] * dp[i - 1 ][j] % MOD) % MOD;
34 long long ans = 0 , tmp = 0 ;
35 for ( int i = M - 1 ;i >= 0 ;i -- )
36 {
37 ans = ans * 10 % MOD;
38 tmp = (tmp + dp[ 9 ][i]) % MOD;
39 ans = ans + tmp;
40 }
41 return ans;
42 }
43
44 int main()
45 {
46 preprocess();
47 int cases;
48 scanf( " %d " , & cases);
49 while (cases -- )
50 {
51 for ( int i = 1 ;i < N;i ++ )
52 scanf( " %d " , & a[i]);
53 long long ans = 0 ;
54 for ( int i = 1 ;i < N;i ++ )
55 {
56 if (a[i] > 0 )
57 {
58 a[i] -- ;
59 int add = (DP() * i) % MOD;
60 ans = (ans + add) % MOD;
61 a[i] ++ ;
62 }
63 }
64 printf( " %lld\n " , ans);
65 }
66 return 0 ;
67 }

手赛继续超级低迷……什么时候能好好发挥一下呀

你可能感兴趣的:(补一个解题报告)