您的位置:首页 > 产品设计 > UI/UE

[hackerrank]Unique Divide And Conquer

2016-11-15 16:01 330 查看

题目大意

有多少种不同的n个节点的带编号无根树点分治过程唯一?

DP

设f[i]表示i个节点时的答案。

g[i]表示i个节点组成的森林,森林中每一个棵树都是点分治过程唯一的,而且是有根树,有多少种。

对于f的计算,首先重心有i种编号,然后思考除去重心后的森林部分,可以用g[i-1]表示除了该层外点分治过程唯一的方案,然后减去非法的情况。

非法情况包括:删去点不是重心,点分治过程不唯一。

两种情况可以合成一种:森林中存在一颗树的子树大小为j,且j>=(i+1)/2。(可以自己探究为什么是(i+1)/2,这里的除法当然是整除)

那么我们这个j,首先这棵树的点分治过程必须唯一,而且还要是有根树(对应g的方案),那么根的方案可以有j种选择(注意这里为什么不会算重,不一样的选根可能使得树的形态一致,但编号一定不一致。如果编号也一致,则原来一定算重了!)。同时我们要从i-1种编号中选j个组成这棵树,于是得到f的递推式。

f[i]=i∗(g[i−1]−∑i+12j=1f[j]∗j∗g[i−j−1]∗Cji−1)

再来考虑g如何递推。

为了不算重,我们思考如下过程:

每次删掉当前编号最小点所在的树。

那么一颗森林删除方法唯一。

因此枚举编号最小点所在的树大小j,这棵树点分治过程唯一且有根,同样是f[j]*j,其余是g[i-j],然后编号最小点一定在,因此我们要从i-1个编号选出j-1个编号给这颗树。

g[i]=∑ij=1f[j]∗j∗g[i−j]∗Cj−1i−1

递推f和g,f
就是答案。

#include<cstdio>
#include<algorithm>
#define fo(i,a,b) for(i=a;i<=b;i++)
#define fd(i,a,b) for(i=a;i>=b;i--)
using namespace std;
typedef long long ll;
const int maxn=3000+10;
int f[maxn],g[maxn],fac[maxn],inv[maxn];
int i,j,k,l,t,n,mo;
int quicksortmi(int x,int y){
if (!y) return 1;
int t=quicksortmi(x,y/2);
t=(ll)t*t%mo;
if (y%2) t=(ll)t*x%mo;
return t;
}
int C(int n,int m){
if (n<m) return 0;
return (ll)fac
*inv[m]%mo*inv[n-m]%mo;
}
int main(){
scanf("%d%d",&n,&mo);
fac[0]=1;
fo(i,1,n) fac[i]=(ll)fac[i-1]*i%mo;
inv
=quicksortmi(fac
,mo-2);
fd(i,n-1,0) inv[i]=(ll)inv[i+1]*(i+1)%mo;
g[0]=1;
fo(i,1,n){
t=g[i-1];
fo(j,(i+1)/2,i-1)
t=(t-(ll)f[j]*g[i-j-1]%mo*C(i-1,j)%mo*j%mo)%mo;
f[i]=(ll)i*t%mo;
fo(j,1,i)
g[i]=(g[i]+(ll)g[i-j]*f[j]%mo*C(i-1,j-1)%mo*j%mo)%mo;
}
(f
+=mo)%=mo;
printf("%d\n",f
);
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: