[HAOI2018]苹果树题解

题目链接

大意:不解释

思路:

首先方案数共有n!种,第1个点只有1种选择,第2个点2种选择,生成2个选择的同时消耗一个,第3个点则有3种选择,依次类推共有n!种方案,由于最后答案*n!,故输出的实际上是每种方案的总和。

由于枚举方案是不可行的,考虑枚举边,计算每一个点连向父亲的边的贡献,容易知道贡献为siz*(n-siz)siz为子树大小。所以枚举点与siz即可。再考虑组成子树的形态,与子树外的形态。设当前枚举到i号点,子树大小为siz,则子树内不考虑编号有siz!种形态,考虑编号则有C(n-i,siz-1)种编号组合,则子树内共有siz!*C(n-i,siz-1)种方案;考虑子树外:由于已有i个点,这i个点可以有i!种方案,从第i+siz-1点开始由于以i为根的子树siz已确定,故这个点不能插入到以i为根的子树内,所以只有i-1种选择,这一部分贡献为(i-1)*(i)*(i+1)*……*(n-siz-1),与前面的总和化简得i*(i-1)*(n-siz-1)!。则当前枚举的点isiz对答案的贡献为siz*(n-siz)*i*(i-1)*(n-siz-1)!*C(n-i,siz-1)*siz!。预处理出组合和阶乘枚举点isiz统计即可。时间复杂度O(n^2);

 1 #include <iostream>
 2 #include <cstdio>
 3 #include <memory.h>
 4 #define r(x) x=read()
 5 #define MAXX 2005
 6 #define MAX(a,b) (a>b?a:b)
 7 #define MIN(a,b) (a<b?a:b)
 8 using namespace std;
 9 typedef long long ll;
10 ll read()
11 {
12   char ch=0;ll w=0,ff=1;
13   while(ch<'0'||ch>'9'){if(ch=='-')ff=-1;ch=getchar();}
14   while(ch>='0'&&ch<='9'){w=w*10+ch-'0';ch=getchar();}
15   return ff*w;
16 }
17 ll P,n,fac[MAXX][MAXX],jie[MAXX],ans;
18 int main()
19 {
20   jie[0]=1ll;
21   r(n),r(P);
22   for(int i=0;i<=n;++i)
23     for(int j=0;j<=i;++j)
24       fac[i][j]=(j==0?1:(fac[i-1][j-1]+fac[i-1][j])%P);
25   for(int i=1;i<=2000;++i) jie[i]=jie[i-1]*i*1ll%P;
26   for(int i=2;i<=n;++i)
27     for(int sz=1;sz<=n-i+1;++sz)
28       ans=(ans+(sz*1ll*(n-sz)*1ll%P*jie[sz]%P*jie[n-sz-1]%P*i*(i-1)%P*fac[n-i][sz-1])%P)%P;
29   printf("%lld",ans%P);
30   return 0;
31 }

猜你喜欢

转载自www.cnblogs.com/dah1314/p/11708729.html