zoukankan      html  css  js  c++  java
  • BZOJ 1005 明明的烦恼 Prufer序列+组合数学+高精度

    题目大意:给定一棵n个节点的树的节点的度数。当中一些度数无限制,求能够生成多少种树

    Prufer序列

    把一棵树进行下面操作:

    1.找到编号最小的叶节点。删除这个节点,然后与这个叶节点相连的点计入序列

    2.重复进行1,直到这棵树仅仅剩下两个节点时,退出


    比方说这个图(来自度受百科)

    最小叶节点为2,删除2,将3计入序列

    最小叶节点为4,删除4,将5计入序列

    最小叶节点为5,删除5,将1计入序列

    最小叶节点为1,删除1。将3计入序列

    图中仅仅剩下两个节点,退出

    于是得到这棵树的Prufer序列为{3,5,1,3}

    这样能够得到一个长度为n-2的序列。非常easy证明,树和Prufer序列是一一相应的

    Prufer序列显然满足一个性质:一个点若度数为d,则一定在Prufer序列中出现了d-1次

    于是这就变成了一个排列组合的问题了

    令每一个已知度数的节点的度数为di,有n个节点,m个节点未知度数。left=(n-2)-(d1-1)-(d2-1)-...-(dk-1)

    已知度数的节点可能的组合方式例如以下

    (n-2)!/(d1-1)!/(d2-1)!/.../(dk-1)!/left!

    剩余left个位置由未知度数的节点任意填补,方案数为m^left

    于是最后有

    ans=(n-2)!/(d1-1)!/(d2-1)!/.../(dk-1)!/left! * m^left

    答案非常显然有高精度。为了避免高精度除法我们能够对每一个阶乘暴力分解质因数,对指数进行加减操作就可以

    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    #define M 1100
    using namespace std;
    typedef long long ll;
    struct abcd{
    	ll xx[400];
    	int cnt;
    	abcd(int x=0)
    	{
    		memset(xx,0,sizeof xx);
    		xx[1]=x;
    		cnt=1;
    	}
    	ll& operator [] (int x)
    	{
    		return xx[x];
    	}
    }ans(1);
    abcd operator *= (abcd &x,abcd &y)
    {
    	int i,j;
    	abcd z;
    	for(i=1;i<=x.cnt;i++)
    		for(j=1;j<=y.cnt;j++)
    			z[i+j-1]+=x[i]*y[j],z[i+j]+=z[i+j-1]/100000000,z[i+j-1]%=100000000;
    	z.cnt=x.cnt+y.cnt;
    	if(!z[z.cnt])
    		--z.cnt;
    	x=z;
    }
    ostream& operator << (ostream& os,abcd &x)
    {
    	int i;
    	printf("%lld",x[x.cnt]);
    	for(i=x.cnt-1;i;i--)
    		printf("%08lld",x[i]);
    	return os;
    }
    int n,m,remain,cnt[M],stack[M],top;
    void Decomposition(int x,int y)
    {
    	int i;
    	for(i=2;i*i<=x;i++)
    		while(x%i==0)
    			cnt[i]+=y,x/=i;
    	if(x^1)
    		cnt[x]+=y;
    }
    void Quick_Power(int i,int y)
    {
    	abcd x(i);
    	while(y)
    	{
    		if(y&1)ans*=x;
    		x*=x;
    		y>>=1;
    	}
    }
    int main()
    {
    	int i,x;
    	cin>>n;remain=n-2;
    	for(i=1;i<=n;i++)
    	{
    		scanf("%d",&x);
    		if(x==-1)
    			++m;
    		else if(x>1)
    			stack[++top]=x-1,remain-=x-1;
    	}
    	for(i=2;i<=n-2;i++)
    		Decomposition(i,1);
    	while(top)
    	{
    		for(i=2;i<=stack[top];i++)
    			Decomposition(i,-1);
    		stack[top--]=0;
    	}
    	for(i=2;i<=remain;i++)
    		Decomposition(i,-1);
    	Decomposition(m,remain);
    	for(i=1;i<=n;i++)
    		if(cnt[i])
    			Quick_Power(i,cnt[i]);
    	cout<<ans<<endl;
    }
    


  • 相关阅读:
    Linux内存初始化
    linux PCI设备初始化过程
    Linux网络地址转换分析
    Linux内核中流量控制
    IPSEC实现
    ip_conntrack 实现
    module_init宏解析
    IP隧道基础研究
    IPV6介绍
    Golang的接口
  • 原文地址:https://www.cnblogs.com/gcczhongduan/p/5253352.html
Copyright © 2011-2022 走看看