【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; 
} 

转载于:https://www.cnblogs.com/heyujun/p/10553365.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值