Luogu4492 [HAOI2018]苹果树 【动态规划】

题目分析:

思路不难想,考虑三个dp状态$f,g,d$。

$g[i]$表示有$i$个点的堆的数量

$d[i]$表示有$i$个点的情况下所有的方案数中点到根的距离和

$f[i]$表示要求的答案。

不难发现$g[i]=i!$,然后$d[i]$就枚举左子树大小,然后把左右子树单独的$d[j]$加起来,最后对于每种方案都加上$i-1$,也就是$d[i] = g[i]*(i-1)+\sum_{j=0}^{i-1}\binom{i-1}{j}*(d[j]*g[i-j-1]+d[i-j-1]*g[j])$。

然后考虑$f[i]$,也是考虑左子树大小然后递归处理$f[j]$,然后再通过$d[i]$处理出到根的距离和,最后再通过$d[i]$之间的乘法求出跨越两个子树的情况,具体看我代码。

代码:

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 
 4 const int maxn = 2050;
 5 
 6 int n,p;
 7 int g[maxn],f[maxn],d[maxn];
 8 int C[maxn][maxn];
 9 
10 void work(){
11     g[0] = 1; d[0] = d[1] = 0;
12     for(int i=1;i<=n;i++) g[i] = 1ll*g[i-1]*i%p;
13     for(int i=2;i<=n;i++){
14     for(int j=0;j<i;j++){
15         int sub=1ll*C[i-1][j]*((1ll*d[j]*g[i-j-1]+1ll*d[i-1-j]*g[j])%p)%p;
16         d[i] += sub; d[i] %= p;
17     }
18     d[i] += 1ll*g[i]*(i-1)%p; d[i] %= p;
19     }
20     f[0] = 0; f[1] = 0;
21     for(int i=2;i<=n;i++){
22     for(int j=0;j<i;j++){
23         int sub=1ll*C[i-1][j]*(1ll*f[j]*g[i-j-1]%p+1ll*f[i-j-1]*g[j]%p)%p;
24         int lt=1ll*C[i-1][j]*((d[j]+1ll*g[j]*j)%p)%p*g[i-j-1]%p;
25         int rt=1ll*C[i-1][j]*((d[i-j-1]+1ll*g[i-j-1]*(i-j-1))%p)%p*g[j]%p;
26         int crs=1ll*lt*(i-1-j)%p,cts=1ll*rt*j%p;
27         f[i] += (1ll*sub+lt+rt+crs+cts)%p;
28         f[i] %= p;
29     }
30     }
31     printf("%d\n",f[n]);
32 }
33 
34 int main(){
35     scanf("%d%d",&n,&p);
36     if(p == 1){puts("0");return 0;}
37     for(int i=1;i<=n;i++){
38     C[i][0] = C[i][i] = 1;
39     for(int j=1;j<i;j++){
40         C[i][j] = (C[i-1][j] + C[i-1][j-1])%p;
41     }
42     }
43     work();
44     return 0;
45 }

猜你喜欢

转载自www.cnblogs.com/Menhera/p/10486482.html