【BZOJ1005】[HNOI2008]明明的烦恼
【BZOJ1005】[HNOI2008]明明的烦恼
题面
题解
前置芝士:
\(prufer\)序列
关于此题
设有度数限制的点的个数是\(cnt\),度数为\(d[i]\),令\(sum=\sum_{i=1}^{cnt}(d[i]-1)\)
不同排列的个数为
\[C_{n-2}^{sum}\times\frac{sum!}{\prod_{i=1}^{cnt}(d[i]-1)!}
\]
还剩下\(n-2-sum\)个位置放\(n-cnt\)个点,
经过化简,最后的答案为
\[Ans=(n-cnt)^{n-2-sum}\times\frac{(n-2)!}{(n-2-sum)!\times\prod_{i=1}^{cnt}(d[i]-1)!}
\]
然后要写高精qaq。
\(tips:\)通过分解质因数避免高精除法。
代码
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std;
inline int gi() {
register int data = 0, w = 1;
register char ch = 0;
while (!isdigit(ch) && ch != '-') ch = getchar();
if (ch == '-') w = -1, ch = getchar();
while (isdigit(ch)) data = 10 * data + ch - '0', ch = getchar();
return w * data;
}
const int BASE = 10000;
struct Wint {
int num[2005], size;
Wint() { memset(num, 0, sizeof(num)); size = 0; }
void turn(int v) { size = 0; while (v) num[++size] = v % BASE, v /= BASE; }
void write() { printf("%d", num[size]); for (int i = size - 1; i; i--) printf("%04d", num[i]); }
} ;
Wint operator * (const Wint &a, const int &b) {
Wint c;
for (int i = 1; i <= a.size; i++) c.num[i] = a.num[i] * b;
for (int i = 1; i <= a.size; i++) c.num[i + 1] += c.num[i] / BASE, c.num[i] %= BASE;
int tmp = a.size;
while (c.num[tmp + 1]) ++tmp, c.num[tmp + 1] += c.num[tmp] / BASE, c.num[tmp] %= BASE;
c.size = tmp;
return c;
}
const int MAX_N = 1e4 + 5;
int N, a[MAX_N], cnt, sum;
int p[MAX_N], p1[MAX_N], p2[MAX_N];
void divisor(int *num, int x) {
for (int i = 2; i * i <= x; i++)
while (x % i == 0) ++num[i], x /= i;
if (x != 1) ++num[x];
}
int main () {
#ifndef ONLINE_JUDGE
freopen("cpp.in", "r", stdin);
#endif
N = gi();
for (int i = 1; i <= N; i++) {
a[i] = gi(); if (a[i] == -1) continue;
++cnt, sum += a[i] - 1;
}
if (sum > N - 2) return puts("0") & 0;
for (int i = 1; i <= N - 2; i++) divisor(p1, i);
for (int i = 1; i <= N - 2 - sum; i++) divisor(p2, i);
for (int i = 1; i <= N; i++) {
if (a[i] == -1) continue;
for (int j = 1; j < a[i]; j++) divisor(p2, j);
}
for (int i = 1; i <= N - 2 - sum; i++) divisor(p1, N - cnt);
for (int i = 1; i <= N; i++) p[i] = p1[i] - p2[i];
Wint ans;
ans.turn(1);
for (int i = 1; i <= N; i++)
for (int j = 1; j <= p[i]; j++) ans = ans * i;
ans.write();
putchar('\n');
return 0;
}