BZOJ 1211[HNOI2004]树的计数 - prufer数列
描述
一个有n个结点的树,设它的结点分别为v1, v2, …, vn,已知第i个结点vi的度数为di,问满足这样的条件的不同的树有多少棵。给定n,d1, d2, …, dn,编程需要输出满足d(vi)=di的树的个数。
题解
每颗树都对应以中prufer数列, prufer数列中数出现的个数 $=$ 节点的度数 -1
所以变成了求再prufer数列中, $x$出现次数为$c_x$ 的排列数
答案为$!(N - 2) / \prod\limits_{i = 1}^N{a_i-1}$
直接算会爆LL, 需要分解质因数
另外还需要特判
代码
我发现打了个错误程序,由于某B姓OJ数据太水竟然过了。。。这个是错误的→_→,幸好发现了, 不然要出锅QAQ
1 #include<cstdio> 2 #include<algorithm> 3 #define ll long long 4 #define rd read() 5 using namespace std; 6 7 const int N = 200; 8 9 int pri[N], tot, vis[N], n, cnt[N], sum; 10 ll ans = 1; 11 12 ll fpow(ll a, ll p) { 13 ll re = 1; 14 for(; p; p >>= 1, a = a * a) if(p & 1) re = re * a; 15 return re; 16 } 17 18 int read() { 19 int X = 0, p = 1; char c = getchar(); 20 for(; c > '9' || c < '0'; c = getchar()) if(c == '-') p = -1; 21 for(; c >= '0' && c <= '9'; c = getchar()) X = X * 10 + c - '0'; 22 return X * p; 23 } 24 25 void init() { 26 for(int i = 2; i < N; ++i) { 27 if(!vis[i]) pri[++tot] = i; 28 for(int j = 1; j <= tot && pri[j] * i < N; ++j) { 29 vis[i * pri[j]] = 1; 30 if(i % pri[j] == 0) break; 31 } 32 } 33 } 34 35 void cal(int x, int k) { 36 if(!x || x == 0) return; 37 for(int i = 1; i <= tot && x != 1; ++i) if(x % pri[i] == 0) { 38 while(x % pri[i] == 0) cnt[i] += k, x /= pri[i]; 39 } 40 } 41 42 int main() 43 { 44 init(); 45 n = rd; 46 for(int i = 2; i <= n - 2; ++i) cal(i, 1); 47 for(int i = 1; i <= n; ++i) { 48 int x = rd; 49 if(n != 1 && !x) return printf("0\n"), 0; 50 if(n == 1 && x) return printf("0\n"), 0; 51 if(n == 1 && !x) return printf("1\n"), 0; 52 for(int j = 2; j < x; ++j) cal(j, -1); 53 sum += x - 1; 54 } 55 if(sum != n - 2) return printf("0\n"), 0; 56 for(int i = 1; i <= tot; ++i) ans *= fpow(pri[i], cnt[i]); 57 printf("%lld\n", ans); 58 }