【BZOJ1005】明明的烦恼

【不看题解一辈子想不出来系列之一】

树的prufer编码,具体内容见黄学长博客。。。

http://hzwer.com/3272.html

实现小技巧就是开全局数组记答案各质因数的次数最后统一乘,只有bign*int 所以可以压位

<del>然而数据规模太小所以我什么优化都没有加

#include<cstdlib>
#include<cstdio>
#include<iostream>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<vector>
#include<queue>
using namespace std;
#define maxn 1005
void _read(int &x)
{
	x=0; char ch=getchar(); bool flag=false;
	while(ch<'0' || ch>'9'){if(ch=='-')flag=1; ch=getchar();}
	while(ch>='0' && ch<='9'){x=x*10+ch-'0'; ch=getchar();}
	if(flag)x=-x;
	return ;
}
int n,cnt,sum,d[maxn],pre_cnt,pre[2*maxn],a[2*maxn],b[2*maxn]; 
bool v[2*maxn],wrong;
void getpre(int T)//分解质因数的准备工作 
{
	memset(v,0,sizeof(v));
	for(int i=2;i<=T;i++)if(!v[i])
	{
		pre[++pre_cnt]=i;
		for(int j=i;j*i<=T;j++)v[j*i]=1;
	}
	return ;
}
void Init()
{
	wrong=false;
	_read(n); 
	sum=0;cnt=0;
	for(int i=1;i<=n;i++)_read(d[i]);
	
	for(int i=1;i<=n;i++)if(d[i]==0){wrong=true;return;}
	for(int i=1;i<=n;i++)if(d[i]!=-1){cnt++,sum+=(d[i]-1);}
	getpre(2000);
	return ;
}
void fj(int now)//将某个数的阶乘唯一分解
{
	int t=0;
	memset(b,0,sizeof(b));
	if(now==1)return ;
	for(int i=1;i<=pre_cnt;i++)
	{
		t=pre[i];
		while(t<=now){b[i]+=now/t; t=t*pre[i];}
	}
	return ;
}
void getC(int x,int y)
{
	fj(y);for(int i=1;i<=pre_cnt;i++)a[i]+=b[i];
	fj(x);for(int i=1;i<=pre_cnt;i++)a[i]-=b[i];
	fj(y-x);for(int i=1;i<=pre_cnt;i++)a[i]-=b[i];
	return ;
}
void getA()
{
	fj(sum);for(int i=1;i<=pre_cnt;i++)a[i]+=b[i];
	for(int j=1;j<=n;j++)if(d[j]-1>0)
	{
		fj(d[j]-1);
		for(int i=1;i<=pre_cnt;i++)a[i]-=b[i];
	}
	return ;
}
void getPower()
{
	memset(b,0,sizeof(b));
	if(cnt<n)
	{
		int t=n-cnt;
		for(int i=1;i<=pre_cnt;i++)
		{
			if(t<=1)break;
			while(t%pre[i]==0 && t>0){b[i]++; t=t/pre[i];}			
		}
		for(int i=1;i<=pre_cnt;i++)b[i]*=(n-2-sum);
	}
	for(int i=1;i<=pre_cnt;i++)a[i]+=b[i];
	return ;
}
struct bign
{
	int a[4005],n;
	bign()
	{
		memset(a,0,sizeof(a)); n=1; a[1]=1;
	}
	friend bign operator *(bign x,int y)
	{
		bign b;
		b.n=x.n+5;
		for(int i=1;i<=b.n;i++)b.a[i]=x.a[i]*y;
		for(int i=1;i<=b.n;i++)if(b.a[i]>=10){b.a[i+1]+=b.a[i]/10; b.a[i]=b.a[i]%10;}
		while(b.a[b.n]==0)b.n--;
		return b;
	}
}ans;
void Output()
{
	
	for(int i=1;i<=pre_cnt;i++)
	{
		for(int j=1;j<=a[i];j++)ans=ans*pre[i];
	}
	for(int i=ans.n;i>=1;i--)putchar(ans.a[i]+'0');
	putchar('\n');
	return ;
}
void work()
{
	memset(a,0,sizeof(a));//a数组记录答案中各质因数的次数
	//计算组合数 
	getC(sum,n-2);
	//计算可重集排列
	getA(); 
	//计算无限制项
	getPower();
	Output();
	return ; 
}
int main()
{
	freopen("in.txt","r",stdin);
	Init();
	if(n==1)
	{
		if(d[1]==0)printf("1\n");
		else printf("0\n");
		return 0;
	}
	if(sum<=n-2 && wrong==false)work();
	else printf("0\n");
	return 0;
}


 


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值