【Prufer数列/组合数学】[HNOI2008][HYSBZ/BZOJ1005]明明的烦恼
来源:互联网 发布:北京美工培训班学费 编辑:程序博客网 时间:2024/04/26 19:22
题目链接
分析
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个点的度数为
如果没有不确定度数的点,那么方案数为全排列。
然而有不确定的点那么,这些确定的点会放在n-2个位置中的sum个,所以
剩下的n-2-sum个位置,每个位置都可以从n-cnt个点中任意选择一个来放。
用高精度计算,除法不好做,可先分解质因数。
代码
#include<cstdio>#include<algorithm>#include<cmath>#include<cstring>using namespace std;#define MAXN 1000#define MAXLEN 3000int 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[n]+=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();}
0 0
- 【Prufer数列/组合数学】[HNOI2008][HYSBZ/BZOJ1005]明明的烦恼
- [Prufer数列] BZOJ1005: [HNOI2008]明明的烦恼
- 【Prufer编码+组合】BZOJ1005(HNOI2008)[明明的烦恼]题解
- [BZOJ1005][HNOI2008]明明的烦恼(prufer计数+组合数)
- [prufer序列]BZOJ1005: [HNOI2008]明明的烦恼
- bzoj1005: [HNOI2008]明明的烦恼 [prufer序列]
- [bzoj1005][prufer][HNOI2008]明明的烦恼
- HYSBZ/BZOJ 1005 [HNOI2008] 明明的烦恼 - Prufer编码&组合数学&高精度 此乃神题!
- 【BZOJ1005】明明的烦恼(HNOI2008)-Prufer序列+组合计数+高精度
- [BZOJ1005]HNOI2008 明明的烦恼|prufer编码|排列组合
- Prufer编码 & [bzoj 1005] [HNOI2008]明明的烦恼:Prufer编码,组合数学,高精度
- BZOJ1005 明明的烦恼 (prufer序列 组合数 高精度)
- bzoj1005: [HNOI2008]明明的烦恼
- bzoj1005【hnoi2008】明明的烦恼
- BZOJ1005[HNOI2008]明明的烦恼
- bzoj1005: [HNOI2008]明明的烦恼
- 【BZOJ1005】【HNOI2008】明明的烦恼
- [BZOJ1005][HNOI2008]明明的烦恼
- 3351: [ioi2009]Regions
- 数据结构基础之栈与递归
- 数组方法归纳
- Win10关闭休眠方法
- 【连通分量】BZOJ 1093: [ZJOI2007]最大半连通子图
- 【Prufer数列/组合数学】[HNOI2008][HYSBZ/BZOJ1005]明明的烦恼
- Python开发简单爬虫(笔记)
- 为什么虚拟机里面的ubuntu不能共享windows的文件. /mnt/hgfs无效
- 记录下集中谷歌搜索方法
- 计算机网络3--计算机网络结构
- ubuntu 安装 paramon 集群监控前台
- MATLAB图像处理工具箱
- [Andoird自定义View]打分控件
- JavaScript菜鸟教程Object1