【BZOJ1005】[HNOI2008]明明的烦恼

【BZOJ1005】[HNOI2008]明明的烦恼

题面

bzoj

洛谷

题解

前置芝士:

\(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; 
} 
posted @ 2019-03-18 17:09  heyujun  阅读(174)  评论(0编辑  收藏  举报