• 【BZOJ1005/1211】[HNOI2008]明明的烦恼/[HNOI2004]树的计数 Prufer序列+高精度


    【BZOJ1005】[HNOI2008]明明的烦恼

    Description

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

    Input

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

    Output

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

    Sample Input

    3
    1
    -1
    -1

    Sample Output

    2

    HINT

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

    题解:每个点在Prufer序列中出现的次数=这个点的度数-1,所以我们令m为已经确定度数的点的数量,$tot=sum D[i]-1$,那么其他位置可以随便选,方案数为$(n-2-tot)^{n-m}$。然后这些已经确定的位置可以随便排列,方案数为$C_{n-2}^{tot} imes C_{tot}^{d1} imes C_{tot-d1}^{d2} imes ...$。

    但显然结果是爆longlong的,不过可以确定答案一定是个整数。为了避免高精度除法,我们可以将分子和分母都拆成质数的几次方形式,然后分子分母做减法,最后用高精度将这些质数乘起来就行了。

    1211那题需要特判:tot!=n-2输出0;n>1且di=0输出0;di>=n输出0。

    #include <cstdio>
    #include <iostream>
    #include <cstring>
    using namespace std;
    const int maxn=1010;
    int n,m,tot,num;
    int pri[maxn],np[maxn],lp[maxn],s[maxn],d[maxn],B[maxn];
    struct Cbig
    {
    	int a[10010],len;
    	Cbig() {memset(a,0,sizeof(a)),len=1;}
    	int & operator [] (int b) {return a[b];}
    	void operator *= (const int &b)
    	{
    		for(int i=1;i<=len;i++)	a[i]*=b;
    		for(int i=1;i<=len;i++)	a[i+1]+=a[i]/1000,a[i]%=1000;
    		while(a[len+1])	len++;
    		while(len&&!a[len])	len--;
    	}
    }ans;
    inline void add(int x,int v)
    {
    	while(x!=1)	s[lp[x]]+=v,x/=pri[lp[x]];
    }
    void init()
    {
    	int i,j;
    	for(i=2;i<=n;i++)
    	{
    		if(!np[i])	pri[++num]=i,lp[i]=num;
    		for(j=1;j<=num&&i*pri[j]<=n;j++)
    		{
    			np[i*pri[j]]=1,lp[i*pri[j]]=j;
    			if(i%pri[j]==0)	break;
    		}
    	}
    }
    inline int rd()
    {
    	int ret=0,f=1;	char gc=getchar();
    	while(gc<'0'||gc>'9')	{if(gc=='-')f=-f;	gc=getchar();}
    	while(gc>='0'&&gc<='9')	ret=ret*10+gc-'0',gc=getchar();
    	return ret*f;
    }
    int main()
    {
    	n=rd(),tot=0,init();
    	int i;
    	for(i=1;i<=n;i++)
    	{
    		d[i]=rd();
    		if(d[i]==0)
    		{
    			printf("0");
    			return 0;
    		}
    		if(d[i]>0)	d[i]--,m++,tot+=d[i];
    	}
    	if(tot>n-2)
    	{
    		printf("0");
    		return 0;
    	}
    	ans.a[1]=ans.len=1;
    	for(i=1;i<=n-2-tot;i++)	ans*=(n-m);
    	B[n-2]++,B[n-2-tot]--;
    	for(i=1;i<=n;i++)	if(d[i]>=0)	B[d[i]]--,tot-=d[i];
    	for(i=n-2;i>=1;i--)	B[i]+=B[i+1],add(i,B[i]);
    	for(i=1;i<=n-2;i++)	while(s[i]--)	ans*=pri[i];
    	printf("%d",ans.a[ans.len]);
    	for(i=ans.len-1;i>=1;i--)	printf("%03d",ans.a[i]);
    	return 0;
    }//5 1 1 -1 -1 -1
    #include <cstdio>
    #include <iostream>
    #include <cstring>
    using namespace std;
    const int maxn=1010;
    int n,tot,num;
    int pri[maxn],np[maxn],lp[maxn],s[maxn],d[maxn],B[maxn];
    struct Cbig
    {
    	int a[10010],len;
    	Cbig() {memset(a,0,sizeof(a)),len=1;}
    	int & operator [] (int b) {return a[b];}
    	void operator *= (const int &b)
    	{
    		for(int i=1;i<=len;i++)	a[i]*=b;
    		for(int i=1;i<=len;i++)	a[i+1]+=a[i]/1000,a[i]%=1000;
    		while(a[len+1])	len++;
    		while(len&&!a[len])	len--;
    	}
    }ans;
    inline void add(int x,int v)
    {
    	while(x!=1)	s[lp[x]]+=v,x/=pri[lp[x]];
    }
    void init()
    {
    	int i,j;
    	for(i=2;i<=n;i++)
    	{
    		if(!np[i])	pri[++num]=i,lp[i]=num;
    		for(j=1;j<=num&&i*pri[j]<=n;j++)
    		{
    			np[i*pri[j]]=1,lp[i*pri[j]]=j;
    			if(i%pri[j]==0)	break;
    		}
    	}
    }
    inline int rd()
    {
    	int ret=0,f=1;	char gc=getchar();
    	while(gc<'0'||gc>'9')	{if(gc=='-')f=-f;	gc=getchar();}
    	while(gc>='0'&&gc<='9')	ret=ret*10+gc-'0',gc=getchar();
    	return ret*f;
    }
    int main()
    {
    	n=rd(),tot=0,init();
    	int i;
    	for(i=1;i<=n;i++)
    	{
    		d[i]=rd();
    		if((n!=1&&d[i]==0)||d[i]>=n)
    		{
    			printf("0");
    			return 0;
    		}
    		d[i]--,tot+=d[i];
    	}
    	if(tot!=n-2)
    	{
    		printf("0");
    		return 0;
    	}
    	ans.a[1]=ans.len=1;
    	B[n-2]++,B[n-2-tot]--;
    	for(i=1;i<=n;i++)	if(d[i]>=0)	B[d[i]]--,tot-=d[i];
    	for(i=n-2;i>=1;i--)	B[i]+=B[i+1],add(i,B[i]);
    	for(i=1;i<=n-2;i++)	while(s[i]--)	ans*=pri[i];
    	printf("%d",ans.a[ans.len]);
    	for(i=ans.len-1;i>=1;i--)	printf("%03d",ans.a[i]);
    	return 0;
    }//5 1 1 -1 -1 -1
  • 相关阅读:
    CCF201712-2游戏
    SpringMVC(未完待续....)
    MySQL----商品表及商品分类表例子
    Spring----属性注入的三种方式
    Spring的配置文件applicationContext.xml
    Spring----getBean的四种用法
    Spring----工厂模式
    spring第一个小例子(Spring_xjs1)
    JSON
    XStream(可把JavaBean转换成XML的小工具)
  • 原文地址:https://www.cnblogs.com/CQzhangyu/p/7787599.html
Copyright © 2020-2023  润新知