1. 程式人生 > >【BZOJ1005/1211】[HNOI2008]明明的煩惱/[HNOI2004]樹的計數 Prufer序列+高精度

【BZOJ1005/1211】[HNOI2008]明明的煩惱/[HNOI2004]樹的計數 Prufer序列+高精度

數量 最終 避免 desc inpu content bzoj1005 zoj mil

【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} \times C_{tot}^{d1} \times C_{tot-d1}^{d2} \times ...$。

但顯然結果是爆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

【BZOJ1005/1211】[HNOI2008]明明的煩惱/[HNOI2004]樹的計數 Prufer序列+高精度