BZOJ 1005[HNOI2008]明明的烦恼
Description
自从明明学了树的结构,就对奇怪的树产生了兴趣......给出标号为1到N的点,以及某些点最终的度数,允许在
任意两点间连线,可产生多少棵度数满足要求的树?
Input
第一行为N(0 < N < = 1000),
接下来N行,第i+1行给出第i个节点的度数Di,如果对度数不要求,则输入-1
Output
一个整数,表示不同的满足要求的树的个数,无解输出0
Solution
首先我们知道了prufer数列 传送门.
也知道prufer数列中某个数字出现的次数 = 节点的度数 - 1
现题目要求某些节点的读书固定, 就是让它在数列中出现的次数固定。
先不考虑不存在树的情况(特判就懒得写了
定义$d_i$为节点$i$的度数, $sum = \sum\limits_{i=1}^n{d_i-1}$ , $t$ 为被要求的节点的个数
则数列中固定的数的位置方案有 $C^{sum}_{n-1} \times sum! / \prod\limits_{i=1}^n{(d_i-1)!}$种,
剩下 来$n-t$个节点与剩下来$n-sum-2$个位置, 有$(n-t)^{n-2-sum}$种方案
两部分不影响, 乘起来就可以得出答案。
数据有点大, 需要打高精乘法。
Code
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 #define rd read() 5 #define ll long long 6 using namespace std; 7 8 const int N = 2e3; 9 const ll base = 1e8; 10 11 int n, sum, c[N], x; 12 int pri[N], tot, vis[N], cnt[N]; 13 14 struct BIG { 15 ll s[N]; 16 BIG() { 17 s[0] = 0; 18 memset(s, 0,sizeof(s)); 19 } 20 BIG operator * (const ll &t) const{ 21 BIG re; 22 re.s[0] = s[0]; 23 ll x = 0; 24 for(int i = 1; i <= s[0]; ++i) { 25 re.s[i] = s[i] * t; 26 x = re.s[i] / base; 27 re.s[i] %= base; 28 } 29 if(x) re.s[++re.s[0]] = x; 30 return re; 31 } 32 }ans; 33 34 int read() { 35 int X = 0, p = 1; char c = getchar(); 36 for(; c > '9' || c < '0'; c = getchar()) if(c == '-') p = -1; 37 for(; c >= '0' && c <= '9'; c = getchar()) X = X * 10 + c - '0'; 38 return X * p; 39 } 40 41 ll fpow(ll a, ll p) { 42 if(!a) return 1; 43 ll re = 1; 44 for(; p; p >>= 1, a = a * a) if(p & 1) re = re * a; 45 return re; 46 } 47 48 void init() { 49 for(int i = 2; i < N; ++i) { 50 if(!vis[i]) pri[++tot] = i; 51 for(int j = 1; j <= tot && pri[j] * i < N; ++j) { 52 vis[i * pri[j]] = 1; 53 if(i % pri[j] == 0) break; 54 } 55 } 56 } 57 58 void cal(int x, int k) { 59 for(int i = 1; x != 1 && i <= tot; ++i) if(x % pri[i] == 0) { 60 while(x % pri[i] == 0) cnt[i] += k, x /= pri[i]; 61 } 62 } 63 64 void print(BIG t) { 65 printf("%lld", t.s[t.s[0]]); 66 for(int i = t.s[0] - 1; i; --i) printf("%08lld", t.s[i]); 67 putchar('\n'); 68 } 69 70 int main() 71 { 72 n = rd; 73 init(); 74 ans.s[0] = 1; 75 ans.s[1] = 1; 76 for(int i = 1; i <= n; ++i) { 77 c[i] = rd; 78 if(c[i] > 0 && n == 1) return printf("0\n"), 0; 79 if(c[i] != -1) { 80 if(c[i] == 0 && n != 1) return printf("0\n"), 0; 81 c[i]--; x++; 82 sum += c[i]; 83 for(int j = 2; j <= c[i]; ++j) cal(j, -1); 84 } 85 } 86 if(n == 1) return printf("1\n"), 0; 87 if(sum > n - 2) return printf("0\n"), 0; 88 if(x == n && sum != n - 2) return printf("0\n"), 0; 89 90 for(int i = 1; i <= n - 2; ++i) cal(i, 1); 91 for(int i = 1; i <= n - sum - 2; ++i) cal(i, -1); 92 93 for(int i = 1; i <= n - sum - 2; ++i) ans = ans * (n - x); 94 95 for(int i = 1; i <= tot; ++i) 96 for(int j = 1; j <= cnt[i]; ++j) ans = ans * pri[i]; 97 print(ans); 98 }