zoukankan      html  css  js  c++  java
  • [BZOJ1005] HNOI2008 明明的烦恼

    问题描述

    自从明明学了树的结构,就对奇怪的树产生了兴趣......给出标号为1到N的点,以及某些点最终的度数,允许在
    任意两点间连线,可产生多少棵度数满足要求的树?

    输入格式

    第一行为N(0 < N < = 1000),
    接下来N行,第i+1行给出第i个节点的度数Di,如果对度数不要求,则输入-1

    输出格式

    一个整数,表示不同的满足要求的树的个数,无解输出0

    样例输入

    3
    1
    -1
    -1

    样例输出

    2

    样例解释

    两棵树分别为1-2-3;1-3-2

    解析

    接下来我们需要使用Purfer Sequence。

    Purfer Sequence

    将一棵无根树按照以下原则构造出来的序列称为这棵树的Purfer Sequence。若树的大小为n,则这样的序列长度为n-2。

    原则:每次找到当前树中度数为1的、编号最小的点,将这个点的父亲加入序列中,同时将这个点从树上删去

    这个序列有许多性质,比如每个Purfer Sequence 对应唯一的一棵无根树。但是,我们要用的是另一个非常重要的性质:

    对于树上的每一个节点(i),设度数为(d_i),那么(i)在序列中会出现(d_i-1)次。

    有了这个性质之后,再看这道题。假设我们已经知道了每个点的度数,那么满足条件的无根树的方案数为:

    [ans=frac{(n-2)!}{prod_{i=1}^{n}(d_i-1)!} ]

    这就是一个可重排列的公式。但是,现在有些点是没有限制的,而序列长度只能是(n-2)。所以,记:

    [cnt=sum_{i=1}^n[d_i!=-1]\sum=sum_{i=1}^n[d_i!=-1] imes (d_i-1) ]

    那么,一个合法的方案就是在(n-2)个位置中选择(sum)个,这些位置进行可重排列。剩下的位置可以在(n-cnt)个点中随便放。方案数为:

    [ans=C_{n-2}^{sum}frac{sum!}{prod_{i=1}^n [d_i!=-1] imes (d_i-1)!} imes (n-cnt)^{n-2-sum} ]

    化简可得:

    [ans=frac{(n-2)!}{(n-2-sum)! imes prod_{i=1}^n [d_i!=-1] imes (d_i-1)!} imes (n-cnt)^{n-2-sum} ]

    当然,单是这么写还需要高精度乘除法。我们可以利用质因数分解,将最后的答案变为质因数相乘的形式,然后就可以用高精度乘低精度计算了。

    注意,Purfer Sequence 可以解决大部分与度数有关的树的计数问题。

    PS:以上关于Purfer Sequence的解释过于简陋,如需详解可以百度

    代码

    #include <iostream>
    #include <cstdio>
    #define N 1000002
    using namespace std;
    int n,sum,cnt,i,j,k,d[N],p[N],c[N],num,ans[N],l;
    bool vis[N];
    int read()
    {
    	char c=getchar();
    	int w=0,f=1;
    	while(c<'0'||c>'9'){
    		if(c=='-') f=-1;
    		c=getchar();
    	}
    	while(c<='9'&&c>='0'){
    		w=w*10+c-'0';
    		c=getchar();
    	}
    	return w*f;
    }
    void change(int x,int op)
    {
    	for(int i=1;i<=num;i++){
    		while(x%p[i]==0) c[p[i]]+=op,x/=p[i];
    	}
    }
    int main()
    {
    	n=read();
    	for(i=2;i<=1000;i++){
    		if(!vis[i]){
    			p[++num]=i;
    			for(j=i;j<=1000;j+=i) vis[j]=1;
    		}
    	}
    	for(i=1;i<=n;i++){
    		d[i]=read();
    		if(d[i]!=-1){
    			cnt++;sum+=d[i]-1;
    			for(j=2;j<=d[i]-1;j++) change(j,-1);
    		}
    	}
    	if(n==1){
    		if(d[1]>0) puts("0");
    		else puts("1");
    		return 0;
    	}
    	else if(n==2){
    		if((d[1]!=1&&d[1]!=-1)||(d[2]!=1&&d[2]!=-1)) puts("0");
    		else puts("1");
    		return 0;
    	}
    	for(i=1;i<=n;i++){
    		if(d[i]==0||d[i]>=n){
    			puts("0");
    			return 0;
    		}
    	}
    	for(i=2;i<=n-2;i++) change(i,1);
    	for(i=2;i<=n-sum-2;i++) change(i,-1);
    	for(i=1;i<=n-sum-2;i++) change(n-cnt,1);
    	ans[1]=l=1;
    	for(i=2;i<=1000;i++){
    		for(j=1;j<=c[i];j++){
    			int tmp=0;
    			for(k=1;k<=l;k++){
    				ans[k]=ans[k]*i+tmp;
    				tmp=0;
    				if(ans[k]>=10) tmp=ans[k]/10,ans[k]=ans[k]%10;
    			}
    			while(tmp) ans[++l]=tmp%10,tmp/=10;
    		}
    	}
    	for(i=l;i>=1;i--) printf("%d",ans[i]);
    	puts("");
    	return 0;
    }
    
  • 相关阅读:
    java 单例模式
    java 设计模式
    android 设计模式
    我的坦克兵爷爷也曾扬威世界
    我的坦克兵爷爷也曾扬威世界
    LD_LIBRARY_PATH设置问题
    LD_LIBRARY_PATH设置问题
    销售员和程序员
    销售员和程序员
    如何成为Python高手
  • 原文地址:https://www.cnblogs.com/LSlzf/p/12623848.html
Copyright © 2011-2022 走看看