1. 程式人生 > >HNOI2008 明明的煩惱 (purfer序列 + 組合數學)

HNOI2008 明明的煩惱 (purfer序列 + 組合數學)

導致 位置 設有 alt 精度 amp problem stream cal

傳送門

這道題題意描述很清楚,不過我自己做的時候確實是一頭霧水……又看了題解,發現要用到一個新知識,叫purfer序列。

我們來簡單說一下什麽是purfer序列。它可以被看作一種樹的表現形式。一棵含有n個節點的樹可以用一個長度為n-2的purfer序列表示,而其中每一個樹都是1~n之間的一個數。

每一棵樹,它都有自己唯一的purfer序列,反過來,對於每一個purfer序列,都能獲得唯一的一棵樹。也就是說,樹與其purfer序列一一對應。

先說一下怎麽求purfer序列。首先找出這棵樹中,節點編號數最小的一個葉子結點(度數為1,只有連向其父親的一條邊),把與它相連的那個節點加入到purfer序列中,並且將這個節點和與之相連的邊從這棵樹中刪除。重復上述過程n-2步,得到一個長度為n-2的purfer序列和一個只有兩個節點的樹。

(還是偷一下大神的圖來描述這件事)

看看下面的例子:

假設有一顆樹有 5 個節點,四條邊依次為:(1, 2), (1, 3), (2, 4), (2, 5),如下圖所示:

技術分享圖片

第 1 步,選取具有最小標號的葉子節點 3,將與它相連的點 1 作為第 1 個 Purfer Number,並從樹中刪掉節點 3:

技術分享圖片

第 2 步,選取最小標號的葉子節點 1,將與其相連的點 2 作為第 2 個 Purfer Number,並從樹中刪掉點 1:

技術分享圖片

第 3 步,選取最小標號的葉子節點 4,將與其相連的點 2 作為第 3 個 Purfer Number,並從樹中刪掉點 4:

技術分享圖片

最後,我們得到的 Purfer Sequence 為:1 2 2

既然如此,purfer序列就求好了,那我們再說一下怎麽通過purfer序列求它相對應的樹。

先把所有節點的度數賦為1,再加上其在Purfer序列中出現過的次數,得到每一個節點的度。每次選取編號最小的度數為1的節點(比如當前枚舉到第i個點),將這個節點和Purfer序列中第i個數所對應的點連一條邊,並且把這兩個點的度數-1。最後得到兩個度數為1的點,我們再把他們連邊,加入到樹中。這樣就成功的通過purfer序列求出一棵樹了。

(我們再偷一次大神的圖並且以上面的例子為例描述一下這個過程)(感謝大神orz)

頂點 1 2 3 4 5
2 3 1 1 1

第 1 次執行,選取最小標號度為 1 的點 3 和 Purfer Sequence 中的第 1 個數 1 連邊:

技術分享圖片

將 1 和 3 的度分別減一:

頂點 1 2 3 4 5
1 3 0 1 1

第 2 次執行,選取最小標號度為 1 的點 1 和 Purfer Sequence 中的第 2 個數 2 連邊:

技術分享圖片

將 1 和 2 的度分別減一:

頂點 1 2 3 4 5
0 2 0 1 1

第 3 次執行,將最小標號度為 1 的點 4 和 Purfer Sequence 第 3 個數 2 連邊:

技術分享圖片

將 2 和 4 的度分別減一:

頂點 1 2 3 4 5
0 1 0 0 1

最後,還剩下兩個點 2 和 5 的度為 1,連邊:

技術分享圖片

這樣我們就知道,purfer序列必然與樹是一一對應的。

而且我們還知道了一條性質,一個點在purfer序列中出現的次數等於其度數-1.

那我們來看一下這道題。

首先考慮無解的情況,這個很好判斷,如果任意一個點的度數是0或者大於n-1那麽就無解,否則有解。

之後再看一般的情況。我們假設一共有m個節點是有度數限制的,剩下n-m個節點沒有度數限制。那麽sum = sigma 1~m(d[i] - 1)

也就是說,這些點占據了purfer序列中sum個位置(一共有n-2個位置),所以這次選擇的種類是C(n-2,sum)

之後對於這個長度為sum的序列,我們考慮一下,第一個位置可以填入d[1]-1個數,選擇的方案為C(sum,d[1]-1),那麽在第二個位置可以填d[2]-1個數,選擇的方案就是C(sum-(d[1]-1),d[2]-1)。

這樣推下去,可以得到總的排列數是:(偷一下圖吧(^_^))

技術分享圖片

之後,因為剩下的n-2-sum個位置,每個沒有度數限制,所以可以隨便填,那麽就還有m^(n-2-sum)種情況。把兩者相乘即為答案。

不過寫高精度是不可能的。

我們可以對每個元素都進行質因數分解,開個桶記錄一下每個數的出現次數,最後把他們乘起來就好。可以先行約去分子分母中相同的數。

不過最後一波把所有數乘起來還是要高精的……不過反正是高精乘低精,比較好寫。

寫題的時候還有個小插曲……質因數分解的時候,非常智障的寫成了while(p),結果導致出現了floating point exception :8這麽個錯誤(好像在Windows下是RE)

不管怎麽說,以後要註意啊。

上一下代碼。(這題其實很神奇因為不需要求puffer序列)

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<iostream>
#include<cmath>
#include<queue>
#include<set>
#define rep(i,a,n) for(int i = a;i <= n;i++)
#define per(i,n,a) for(int i = n;i >= a;i--)
#define enter putchar(‘\n‘)
using namespace std;
const int M = 10005;
int n,len,a,sum,k,tot;
int t[10001],ans[M+1],prime[M+1];
bool np[M];
int read()
{
  int ans = 0,op = 1;
  char ch = getchar();
  while(ch < 0 || ch > 9)
  {
      if(ch == -) op = -1;
      ch = getchar();
  }
  while(ch >=0 && ch <= 9)
  {
      ans *= 10;
      ans += ch - 0;
      ch = getchar();
  }
  return ans * op;
}
void euler()
{
    rep(i,2,n)
    {
    if(!np[i]) prime[++tot] = i;
    for(int j = 1;i * prime[j] <= n;j++)
    {
        np[prime[j] * i] = 1;
        if(!(i % prime[j])) break;
    }
    }
}
void C(int a,int b)
{
    rep(i,b+1,a)
    {
    int p = i,d = 1;
    while(p > 1)
    {
        while (!(p % prime[d])) ans[d]++,p /= prime[d];
        d++;
    }
    }
    rep(i,1,a-b)
    {
    int p = i,d = 1;
    while(p > 1)
    {
        while (!(p % prime[d])) ans[d]--,p /= prime[d];
        d++;
    }
    }
}
void calc()
{
    int d = 1;
    while(sum > 1)
    {
    while (!(sum % prime[d])) ans[d] += len,sum /= prime[d];
    d++;
    }
}
int main()
{
    n = read();
    euler();
    len = n-2;
    rep(i,1,n)
    {
    a = read();
    if(!a || a >= n)
    {
        printf("0");
        return 0;
    }
    if(a == -1)
    {
        sum++;
        continue;
    }
    a--,C(len,a),len -= a;
    }
    if(len) calc();
    t[1] = 1,k = 1;
    rep(i,1,170)
    {
    while(ans[i])
    {
        ans[i]--;
        rep(j,1,k) t[j] *= prime[i];
        rep(j,1,k) if(t[j] >= 10) t[j+1] += t[j]/10,t[j] %= 10;
        while(t[k+1]) k++,t[k+1] += t[k] / 10,t[k] %= 10;    
    }
    }
    per(i,k,1) printf("%d",t[i]); enter;
    return 0;
}

HNOI2008 明明的煩惱 (purfer序列 + 組合數學)