xdoj 1067组合数学+动态规划 (一个题断断续续想了半年 233)

题目分析 : (8 4) 可以由(7 4),(6,4),( 4,4) 基础上转化

意味着一个新加入的元素可以按照它加入的方式分类,从而实现动态规划

核心:加入方式 新加入的元素构成转换环的元素个数(n的约数)

eg: (8,4) 新加入元素自己单独一个环 (7,4)

       (6,4)新加入元素自己构成二元环 (6,4)

      (4,4)新加入元素自己构成四元环 (4,4)

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 typedef long long LL;
 4 const LL mod=1e9+7;
 5 const int N=20007;
 6 LL f[N],inv_f[N];// 阶乘和阶乘的逆元
 7 LL dp[N];
 8 LL p[N]; int cnt;
 9 LL q_pow (LL x,LL k) {// 快速幂求逆元
10   LL ans=1;
11   while (k) {
12     if (k&1) ans=ans*x%mod;
13     x=x*x%mod;
14     k=k>>1;
15   }
16   return ans;
17 }
18 LL c (int  n,int  m) {// 组合数c(n,m)
19   if (m>n) return 0;
20   if (m>n-m) m=n-m;
21   LL x=inv_f[n-m]*inv_f[m]%mod;
22   return x*f[n]%mod;
23 }
24 int n,k;
25 int main ()
26 {
27   f[0]=1; dp[0]=dp[1]=1;
28   for (int i=1;i<N;i++) f[i]=i*f[i-1]%mod;
29   inv_f[N-1]=q_pow (f[N-1],mod-2);
30   for (int i=N-2;i>=0;i--) inv_f[i]=inv_f[i+1]*(i+1)%mod;
31   int T; scanf ("%d",&T);
32   while (T--) {
33     scanf ("%d %d",&n,&k);
34     LL sum=0; int cnt=0;
35     for (int i=1;i<=k;i++) 
36       if (k%i==0) {
37         p[++cnt]=i;// 求得约数
38       }
39     for (int i=2;i<=n;i++)  {
40       dp[i]=0;
41       for (int j=1;j<=cnt;j++) {
42         if (i-p[j]<0) break;
43         dp[i]=(dp[i]+c(i-1,p[j]-1)*f[p[j]-1]%mod*dp[i-p[j]]%mod)%mod;
44                      // c[i-1][p[j]-1]  (选取p[j]-1个元素和新加入的元素构成p[j]元环)
45                     //  f[p[j]-1]  p[j]个元素构成j元环的个数 (p[j]-1)!
46                     //  dp[i-p[j]]  剩余的( i-p[j])元素的组合个数 
47       }
48     }
49     printf("%lld
",dp[n]);
50   }
51   return 0;
52 }
抓住青春的尾巴。。。
原文地址:https://www.cnblogs.com/xidian-mao/p/9389328.html