Count & 樹的同構 解題報告

之所以兩道題放在一起,就是因為他們很相似。

題目一:Count

大項堆是具有如下性質的二叉樹:

  1. 它的任意一個父親節點的權值總是比孩子的權值要大
  2. 它是一顆完全二叉樹

先給定這個堆的節點個數N,假如每個節點的權值都為在1—N之間的整數且各不相同,求可能的大項堆的個數。當然,這個個數可能很多,這裡只要求這個個數模K即可。

輸入: 兩個用空格隔開的整數N和Q(N<30,Q<10^9) 輸出: 可能的大項堆的個數模Q

輸入樣例: 3 1000 輸出樣例: 2

題目二:樹的同構

排序二叉樹是我們熟悉的一種數據結構,這裡提到的排序二叉樹都是指根的關鍵字比左子樹上的所有點都大,比右子樹上的都小(假定不存在相同的關鍵字)的排序二叉樹。如果給定一個輸入序列,按照這個序列依次將各個結點插入,我們就得到了一棵排序二叉樹。例如,輸入序列 4 2 1 3 5 對應如下排序二叉樹:2 4 5 1 3。而輸入序列 4 5 2 1 3 以及 4 2 5 1 3 都對應同樣的樹,我們說這三個序列同構。

現在給定一個長度為 N 的輸入序列,求與之同構的序列有多少個(包括這個序列本身)。由於問題的答案可能比較大,只要求輸出答案 mod 10,007 的結果。

Input 第 1 行:一個正整數 N,即輸入序列長度。 第 2 行:N 個 1 到 N 之間的正整數,數字相互之間不重複。

Output 僅有一行,即同構序列數 mod 10,007 的長度。

Sample Input

5
4 2 1 3 5

Sample Output

8

樣例解釋: 即 4 2 1 3 5、4 2 1 5 3、4 2 3 1 5、4 2 3 5 1、4 5 2 1 3、4 5 2 3 1、4 5 3 1 2、4 5 3 2 1 這八組輸入順序。

數據範圍: 對於 100% 的數據,N <= 1,000,每組數據 10 分。 對於 120% 的數據,N <= 100,000,每組數據 20 分。

思路分析

這兩個題目主要在於初始化的不同。Count 可以自己虛擬建樹,而 樹的同構 則必須按要求建樹。建過樹之後,這兩個題目是完全相同的,都是樹形動態規劃。用DP

\[r\]

表示節點r為根的樹所能產生的種類或其mod後的數值。 動態轉移方程為:

dp[r] = dp[r.左樹] * dp[r.右樹] * 求組合數(左右子樹的節點數之和, 左/右子樹的節點數(用數學可輕易證明左/右子樹的節點數的結果相同)) 

其實質是,對於一個父親,如何安排它的孩子的情況呢?那就要挑選出一部分數字(對於題目count)(對於樹的同構是位置)安排給左子樹使用,其餘的數字(或位置)自然就分給右子樹了。用組合而不是排列,原因是對於每一個組合,具體如何排列這些數字或位置,已經由子樹DP的數值所包括題目容易被誤解為使用排列。

完整代碼

//By Ceeji 
//Date: 2009-4-9 
//HAOI test 6 - Problem 1 : count 
//Type : Tree DP 
//State: Accepted 
Program count; 
Const 
   fin='count.in'; 
   fou='count.out'; 
Var 
   n,q,c,ci,i:longint; 
   comb:array[0..31,0..31]of longint; 
   dp:array[0..100,1..2]of int64; 
   tree:array[0..100]of boolean; 

Function GetComb(m,n:longint):longint; 
begin 
   if m=n then exit(1); 
   if n=0 then exit(1); 
   if n=1 then exit(m); 
   if comb[m,n]<>0 then exit(comb[m,n]); 
   comb[m,n]:=((getComb(m-1,n) mod q)+(getComb(m-1,n-1) mod q)) mod q; 
   exit(comb[m,n]); 
end; 

Procedure dpit(p:longint); 
begin 
   if not tree[p] then begin dp[p,1]:=1 mod q; dp[p,2]:=0; exit; end; 
   dpit(p<<1); 
   dpit(p<<1+1); 
   dp[p,2]:=dp[p<<1,2]+dp[p<<1+1,2]+1; 
   dp[p,1]:=getComb(dp[p,2]-1,dp[p<<1,2]); 
   dp[p,1]:=(dp[p,1]*dp[p<<1,1])mod q; 
   dp[p,1]:=(dp[p,1]*dp[p<<1+1,1])mod q; 
end; 

begin 
   assign(input,fin);  reset(input); 
   assign(output,fou); rewrite(output); 
   readln(n,q); 
   close(input); 
   fillchar(comb,sizeof(comb),0); 
   fillchar(tree,sizeof(tree),0); 
   c:=1; ci:=1; 
   for i:=1 to n do 
   begin 
      if c=1 then 
      begin 
        tree[ci]:=true; 
        ci:=1; inc(c); 
      end 
      else begin 
        tree[2<<(c-2)+ci-1]:=true; 
        if ci=2<<(c-2) then begin ci:=1; inc(c); end else inc(ci); 
      end; 
   end; 
   fillchar(dp,sizeof(dp),0); 
   dpit(1); 
   writeln(dp[1,1] mod q); 
   close(output); 
end.

完整代碼:樹的同構

//By Ceeji 
//HAOI test 5 - Problem 2 : Tree 
//Date: 2009-3-31 
//Type : Tree DP 
//State: Accepted 
Program tree; 
Const  
   inf='tree.in'; 
   ouf='tree.out'; 
Type  
   node=record 
          v,l,r:longint; 
        end; 
Var  
   n,i,j,m:longint; 
   t:array[1..1000]of node; 
   comb:array[0..1000,0..1000]of longint; 
   dp:array[0..1000,1..2]of longint; 

Procedure add(j,i:longint); 
begin 
   if j<t[i].v then 
   begin 
      if t[i].l=0 then begin inc(m); t[i].l:=m; t[m].v:=j; end 
      else begin add(j,t[i].l); end; 
   end 
   else 
   begin 
      if t[i].r=0 then begin inc(m); t[i].r:=m; t[m].v:=j; end 
      else begin add(j,t[i].r); end; 
   end; 
end; 

Function getcomb(n,m:longint):longint; 
begin 
   if comb[n,m]<>0 then exit(comb[n,m]); 
   if m=1 then exit(n); 
   if (n=m)or(m=0) then exit(1); 
   comb[n,m]:=(getcomb(n-1,m)+getcomb(n-1,m-1))mod 10007; 
   exit(comb[n,m]); 
end; 

Procedure dpit(p:longint); 
begin 
   if dp[t[p].l,1]=0 then dpit(t[p].l); 
   if dp[t[p].r,1]=0 then dpit(t[p].r); 
   dp[p,1]:=(((dp[t[p].l,1]*dp[t[p].r,1])mod 10007)*getcomb(dp[t[p].l,2]+dp[t[p].r,2],dp[t[p].l,2]))mod 10007; 
   dp[p,2]:=1+dp[t[p].l,2]+dp[t[p].r,2]; 
end;` 

begin 
   assign(input,inf);  reset(input); 
   assign(output,ouf); rewrite(output); 
   readln(n); 
   fillchar(t,sizeof(t),0); m:=1; 
   read(j); t[1].v:=j; 
   for i:=2 to n do 
   begin 
      read(j); 
      add(j,1); 
   end; 
   close(input); 
   fillchar(dp,sizeof(dp),0); 
   fillchar(comb,sizeof(comb),0); 
   dp[0,1]:=1; 
   dp[0,2]:=0; 
   dpit(1); 
   writeln(dp[1,1]); 
   close(output); 
end.
当前页阅读量为: