BZOJ1089 [SCOI2003]严格n元树 【dp + 高精】

Description

  如果一棵树的所有非叶节点都恰好有n个儿子,那么我们称它为严格n元树。如果该树中最底层的节点深度为d
(根的深度为0),那么我们称它为一棵深度为d的严格n元树。例如,深度为2的严格2元树有三个,如下图:

  给出n, d,编程数出深度为d的n元树数目。

Input

  仅包含两个整数n, d( 0   <   n   <   =   32,   0  < =   d  < = 16)

Output

  仅包含一个数,即深度为d的n元树的数目。

Sample Input

【样例输入1】
2 2

【样例输入2】
2 3

【样例输入3】
3 5

Sample Output

【样例输出1】
3

【样例输出2】
21

【样例输出2】
58871587162270592645034001



题解

设f[d]为深度不大于d的n元树的个数,显然答案就是f[d] - f[d - 1]

对于考虑f[d]的根节点,它的每一棵子树方案数都是f[d - 1],用乘法原理:

f[d] = f[d - 1] ^ n + 1【+1考虑只有一个根节点】

边界:f[0] = 1


再者就是高精【好久没写高精乘高精了】,写的时候还需要先调一下


#include<iostream>
#include<cstdio>
#include<string>
#include<cstring>
#include<algorithm>
#define LL long long int
#define REP(i,n) for (int i = 1; i <= (n); i++)
#define fo(i,x,y) for (int i = (x); i <= (y); i++)
#define Redge(u) for (int k = head[u]; k != -1; k = edge[k].next)
using namespace std;
const int maxn = 20,maxm = 205,INF = 1000000000;

int N,D;

struct NUM{
	int n[maxm],len;
	NUM() {memset(n,0,sizeof(n)); len = 0;}
}f[maxn];

inline istream& operator >>(istream& in,NUM& a){
	string s;
	in>>s;
	a.len = s.length();
	for (int i = 0; i < a.len; i++) a.n[i] = s[a.len - i - 1] - '0';
	return in;
}

inline ostream& operator << (ostream& out,const NUM& a){
	if (!a.len) out<<0;
	else {
		for (int i = a.len - 1; i >= 0; i--) out<<a.n[i];
	}
	return out;
}

inline NUM operator *(const NUM& a,const NUM& b){
	NUM c;
	c.len = a.len + b.len + 2;
	int carry = 0,temp;
	for (int i = 0; i < a.len; i++){
		for (int j = 0; j < b.len; j++){
			temp = c.n[j + i] + a.n[i] * b.n[j] + carry;
			c.n[j + i] = temp % 10;
			carry = temp / 10;
		}
		int len = i + b.len;
		while (carry) {
			temp = c.n[len] + carry;
			c.n[len] = temp % 10;
			carry = temp / 10;
			len++;
		}
	}
	while (!c.n[c.len - 1]) c.len--;
	return c;
}

inline NUM operator + (const NUM& a,const int& b){
	NUM c = a;
	int temp = c.n[0] + b,carry = temp / 10;
	c.n[0] = temp % 10;
	for (int i = 1; carry && i < c.len; i++){
		temp = c.n[i] + carry;
		c.n[i] = temp % 10;
		carry = temp / 10;
	}
	if (carry) c.n[c.len++] = carry;
	return c;
}

inline NUM operator - (const NUM& a,const NUM& b){
	NUM c;
	c.len = a.len;
	int carry = 0;
	for (int i = 0; i < a.len; i++){
		c.n[i] = a.n[i] - b.n[i] + carry;
		if (c.n[i] < 0) c.n[i] += 10,carry = -1;
		else carry = 0;
	}
	while (!c.n[c.len - 1]) c.len--;
	return c;
}

inline NUM qpow(NUM a,int b){
	NUM ans; ans.n[0] = ans.len = 1;
	for (; b; b >>= 1,a = a * a)
		if (b & 1) ans = ans * a;
	return ans;
}

int main()
{
	/*cin>>f[0]>>f[1];
	cout<<f[0] * f[1]<<endl;*/
	
	cin>>N>>D;
	if (!D) {cout<<1<<endl;return 0;}
	f[0].n[0] = f[0].len = 1;
	for (int i = 1; i <= D; i++){
		f[i] = qpow(f[i - 1],N) + 1;
	}
	cout<<f[D] - f[D - 1]<<endl;
	return 0;
}

posted @ 2017-11-06 17:52  Mychael  阅读(128)  评论(0编辑  收藏  举报