【Prufer数列/组合数学】[HNOI2008][HYSBZ/BZOJ1005]明明的烦恼
2016-02-02 22:10
363 查看
题目链接
分析
Prufer数列
生成Prufer数列
由一棵树得到它的 Prufer Sequence 总共需要 n-2 步,每一步都在当前的树中寻找具有最小标号的叶子节点(度为 1),将与其相连的点的标号设为 Prufer Sequence 的第 i 个元素,并将此叶子节点从树中删除,直到最后得到一个长度为 n-2 的 Prufer Sequence 和一个只有两个节点的树。所以一个树,只能得到一个唯一的 Prufer Sequence。
由Prufer数列生成树
先将所有编号为 1 到 n 的点的度赋初值为 1,然后加上它在 Prufer Sequence 中出现的次数,得到每个点的度先执行 n-2 步,每一步,选取具有最小标号的度为 1 的点 u 与 Prufer Sequence 中的第 i 个数 v 表示的顶点相连,得到树中的一条边,并将 u 和 v 的度减1
最后再把剩下的两个度为 1 的点连边,加入到树中
所以Prufer Sequence和一个树唯一对应。
得到
定理:一个 Prufer Sequence 和一棵树一一对应
如果还不不清楚,请自行百度
回到这道题
一个点在 Prufer Sequence 出现的次数等于它的度数减一,我们考虑有确定度数的点,给他们编号1~cnt,第i个点的度数为did_i。sum=∑i=1cntdisum=\sum_{i=1}^{cnt}d_i
如果没有不确定度数的点,那么方案数为全排列。
P=sum!∏cnti=1(di−1)P=\frac{sum!}{\prod_{i=1}^{cnt}(d_i-1)}
然而有不确定的点那么,这些确定的点会放在n-2个位置中的sum个,所以C(sumn−2)∗PC{sum \choose n-2}*P
剩下的n-2-sum个位置,每个位置都可以从n-cnt个点中任意选择一个来放。
ans=C(sumn−2)∗P∗(n−cnt)n−2−sum=(n−2)!sum!∗(n−2−sum)!∗sum!∏cnti=1(di−1)∗(n−cnt)n−2−sum=(n−2)!(n−2−sum)!∗∏cnti=1(di−1)∗(n−cnt)n−2−sumans=C{sum \choose n-2}*P*(n-cnt)^{n-2-sum}\\=\frac{(n-2)!}{sum!*(n-2-sum)!}*\frac{sum!}{\prod_{i=1}^{cnt}(d_i-1)}*(n-cnt)^{n-2-sum}=\frac{(n-2)!}{(n-2-sum)!*\prod_{i=1}^{cnt}(d_i-1)}*(n-cnt)^{n-2-sum}
用高精度计算,除法不好做,可先分解质因数。
代码
[code]#include<cstdio> #include<algorithm> #include<cmath> #include<cstring> using namespace std; #define MAXN 1000 #define MAXLEN 3000 int n,d[MAXN+10],p[MAXN+10],pcnt,fcnt[MAXN+10],cnt,sum; bool f[MAXN+10]; void prepare(){ int i,j; for(i=2;i<=n;i++){ if(!f[i]) p[++pcnt]=i; for(j=1;p[j]*i<=n;j++){ f[i*p[j]]=1; if(i%p[j]==0) break; } } } struct hp{ int a[MAXLEN+10]; inline hp(){ memset(a,0,sizeof a); } inline hp(hp &a){ *this=a; } void int2hp(int b){ while(b) a[++a[0]]=b%10,b/=10; if(!a[0]) a[0]++; } inline hp(int b){ memset(a,0,sizeof a); int2hp(b); } void operator*=(const hp &b){ hp c; int len=a[0]+b.a[0],i,j; for(i=1;i<=a[0];i++) for(j=1;j<=b.a[0];j++) c.a[i+j-1]+=a[i]*b.a[j]; for(i=1;i<=len;i++){ c.a[i+1]+=c.a[i]/10; c.a[i]%=10; } while(!c.a[len]&&len) len--; c.a[0]=len; *this=c; } void print(){ for(int i=a[0];i;i--) printf("%d",a[i]); } }ans(1); void Read(int &x){ char c; bool f=0; while(c=getchar(),c!=EOF){ if(c=='-') f=1; if(c>='0'&&c<='9'){ x=c-'0'; while(c=getchar(),c>='0'&&c<='9') x=x*10+c-'0'; ungetc(c,stdin); if(f) x=-x; return; } } } void read(){ Read(n); if(n==1){ Read(d[1]); if(d[1]<=0) puts("1"); else puts("0"); exit(0); } for(int i=1;i<=n;i++){ Read(d[i]); if(d[i]>=n||!d[i]){ puts("0"); exit(0); } if(d[i]>-1) sum+=d[i]-1,cnt++; } if(sum>n-2){ puts("0"); exit(0); } } void de_factor(int n,int d){ int i,t=sqrt(n+0.5); for(i=1;p[i]<=t&&n>1;i++) while(n%p[i]==0) fcnt[p[i]]+=d,n/=p[i]; if(n>1) fcnt +=d; } hp quick_pow(int a,int b){ hp c(a),ret; ret.a[0]=1,ret.a[1]=1; while(b){ if(b&1) ret*=c; c*=c; b>>=1; } return ret; } void solve(){ int i,j; for(i=n-2;i>n-2-sum;i--) de_factor(i,1); for(i=1;i<=n;i++) if(d[i]>1) for(j=d[i]-1;j;j--) de_factor(j,-1); for(i=1;i<=pcnt;i++) ans*=quick_pow(p[i],fcnt[p[i]]); ans*=quick_pow(n-cnt,n-2-sum); } int main() { read(); prepare(); solve(); ans.print(); }
相关文章推荐
- #学习笔记#(30)牛客网JS测试题21~45
- js调试工具Console命令详解
- node.js 资料
- 自己定义html中a标签的title提示tooltip
- 如何实现JSP网页模板 JSP网页母版
- HTML-03表单的创建
- Angular依赖注入详解
- Reactor构架模式
- javascript异步过程
- BZOJ 1013: [JSOI2008]球形空间产生器sphere
- Karma的第一次使用
- 【jQuery基础学习】07 jQuery表单插件-Form
- LeetCode24. Swap Nodes in Pairs简单到一次性通过
- angularjs ng-select ng-options 默认选中项.
- HYSBZ/BZOJ 1005 [HNOI2008] 明明的烦恼 - Prufer编码&组合数学&高精度 此乃神题!
- 网页设计中,相对路径与绝对路径的问题
- JSTL不同版本和EL表达式的关联
- 使用react-native做一个简单的应用-05 navigator的使用
- 如何在JavaScript里取session的值
- 2016/02/02 slicebox(css部分)