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
2 2
【样例输入2】
2 3
【样例输入3】
3 5
Sample Output
【样例输出1】
3
【样例输出2】
21
【样例输出2】
58871587162270592645034001
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; }