[HNOI2008] 明明的煩惱(Prufer 序列 + 高精度) | 錯題本

題目

[HNOI2008] 明明的煩惱

分析

cc 爲規定了度數的點的個數, s=i=1c(di1)s = \sum_{i = 1}^{c} (d_i - 1),先考慮規定度數的點的方案數,就是一個重排列乘上選位置的方案數:s!i=1c(di1)!Cn2s\frac{s!}{\prod_{i = 1}^{c} (d_i - 1)!} \cdot C_{n - 2}^{s} 再考慮沒規定度數的點,隨便安排,所以總方案數爲 s!i=1c(di1)!Cn2s(nc)n2s=s!(nc)n2s(n2s)!i=1c(di1)!\begin{aligned} & \frac{s!}{\prod_{i = 1}^{c} (d_i - 1)!} \cdot C_{n - 2}^{s} \cdot (n - c)^{n - 2 - s} \\ =& \frac{s! \cdot (n - c)^{n - 2 - s}}{(n - 2 - s)! \cdot \prod_{i = 1}^{c} (d_i - 1)!}\end{aligned} 要用高精度。我不太會寫除法,就把每個數分解質因數用類似於取對數優化的方法,最後只用算一次高精度乘法就行了。階乘分解質因數可以 O(n)\approx O(n) 求,但 n1000n \leq 1000 沒必要,暴力打出 1n1 \sim n 的分解質因數的表就行了。總複雜度略大於 O(n2)O(n^2)
注意判斷爲 00 的情況有兩個:

  • di=0d_i = 0
  • s>n1s > n - 1(因爲 ss 就是受限的邊數)。

錯因

  • 沒有判斷 di=0d_i = 0 的情況;
  • 高精度數乘 int 都寫掛了:一次只考慮了進一位,實際上可能進兩位(小於 10001000 的最大質數是三位數)……

代碼

#include <bits/stdc++.h>

const int MAXN = 1000;

int N, D[MAXN + 5];
int Div[MAXN + 5][MAXN + 5];
std::vector<int> Pos[MAXN + 5];

void Mul(int *cur, int x, int inv) {
    for (int i = 0; i < int(Pos[x].size()); i++) {
        int num = Pos[x][i];
        if (inv) cur[num] -= Div[x][num];
        else cur[num] += Div[x][num];
    }
}

void Fac(int *cur, int n, int inv) {
    for (int i = 2; i <= n; i++)
        Mul(cur, i, inv);
}

int Res[MAXN * MAXN + 5], M;

void NumMul(int *num, int &len, int x) {
    for (int i = 1, d = 0; i <= len + 2; i++)
        num[i] = num[i] * x + d, d = num[i] / 10, num[i] %= 10;
    len += 2;
}

int main() {
    scanf("%d", &N);
    for (int i = 1; i <= N; i++) {
        int tmp = i;
        for (int j = 2; j * j <= i; j++) {
            while (tmp % j == 0)
                Div[i][j]++, tmp /= j;
            if (Div[i][j])
                Pos[i].push_back(j);
        }
        if (tmp != 1)
            Div[i][tmp] = 1, Pos[i].push_back(tmp);
    }
    int S = 0, C = 0;
    for (int i = 1; i <= N; i++) {
        scanf("%d", &D[i]);
        if (!D[i])
            return puts("0"), 0;
        if (~D[i])
            S += (D[i] - 1), C++;
    }
    if (S > N - 1)
        return puts("0"), 0;
    int *Ans = Div[0];
    Fac(Ans, N - 2, 0);
    for (int i = 1; i <= N - 2 - S; i++)
        Mul(Ans, N - C, 0);
    Fac(Ans, N - 2 - S, 1);
    for (int i = 1; i <= N; i++)
        if (~D[i])
            Fac(Ans, D[i] - 1, 1);
    Res[M = 1] = 1;
    for (int i = 1; i <= N; i++) {
        while (Ans[i]--)
            NumMul(Res, M, i);
    }
    while (M > 1 && !Res[M])
        M--;
    for (int i = M; i >= 1; i--)
        putchar(Res[i] + '0');
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章