1. 程式人生 > >[樹形dp][組合數] JZOJ P1794 保鏢排隊

[樹形dp][組合數] JZOJ P1794 保鏢排隊

scrip 方案 sca 原理 數據 clear 排列 排隊 ios

Description

【問題背景】
  教主LHX作為知名人物,時刻會有恐怖分子威脅他的生命。於是教主雇傭了一些保鏢來保障他的人生安全。

【題目描述】
  教主一共雇傭了N個保鏢,編號為1~N。每個保鏢雖然身手敏捷武功高強,但是他在其余N-1個保鏢裏,都會有一個“上司”,他會對他的上司言聽計從。但一號保鏢例外,他武功蓋世,不懼怕其余任何保鏢,所以他沒有上司。
  教主LHX會對這N個保鏢進行定期視察。每次視察的時候,首先會讓所有保鏢排隊。
  對於每個保鏢,在他心目中會對他的所有下屬的武功實力排個隊。
  現在教主要求排出來的隊伍滿足:①互為上司-下屬的兩個保鏢,上司在前,下屬在後 ②對於一個保鏢的所有下屬,武功實力較強的在前,較弱的在後。
  教主想知道,總的排隊方法數除以10007的余數是多少。

Input

  輸入的第一行為一個正整數T,表示了數據組數。
  對於每組數據:
  第一行為一個正整數N。
  接下來N行,每行描述一個保鏢。
  第i+1行,會有一個整數K,代表第i個保鏢的下屬個數,接下來K個數,代表第i個保鏢的下屬按照武功實力從高到低的編號。

Output

  輸出包括C行,每行對於每組數據輸出方案數mod 10007後的結果。

Sample Input

2
5
2 2 3
2 4 5
0
0
0
7
2 2 3
2 4 5
2 6 7
0
0
0
0

Sample Output

3
10

Hint

【樣例說明】
  對於第1組數據,有以下3種排列是合法的:
  1 2 4 3 5
  1 2 3 4 5
  1 2 4 5 3
  同時滿足了1在2與3之前且2在3之前,2在4與5之前且4在5之前

【數據規模】
  對於20%的數據,有N ≤ 9;
  對於40%的數據,有對於所有K,有K ≤ 2;
  對於60%的數據,有N ≤ 100;
  對於100%的數據,有T ≤ 10,N ≤ 1000,K ≤ N。

題解

  • 首先要明確的是,屬於不同子樹的點可以混在一起,只要同一子樹的有序就可以了。

  • 如果一個結點有兩個兒子l,r,並且l排在r的前面,那麽總的方法數就是f[l]*f[r]*C(size[l]+size[r]-1,size[l]-1)

  • 其含義是:容易想到,根據乘法原理,總共會有f[l]*f[r]種情況,對於每種情況,都可以有C(size[l]+size[r]-1,size[l]-1)種合並方法

  • 子樹總共有size[l]+size[r]個結點(不算根結點),因為l必排最前面,所以剩下的位置就是size[l]+size[r]-1個,其中屬於l的總共就有size[l]-1個,這個就直接用組合數就算出來了

  • 然後再一個個往後合並就好了
  • 組合數可以直接用楊輝三角推出來,如果現算的話時間復雜度就比較高。尤其是RQNOJ的測評機都很蝸牛。

代碼

 1 #include<cstdio>
 2 #include<iostream>
 3 #include<algorithm>
 4 using namespace std;
 5 int n,c[2500][2500],map[2500][2500],s[2500],f[2500],t;  
 6 void dp(int x)
 7 {
 8     int t; 
 9     f[x]=1; s[x]=0;
10     for (int i=map[x][0];i>=1;i--)
11     {
12            t=map[x][i];
13            dp(t);
14            f[x]=(((f[x]*f[t])%10007)*(c[s[t]+s[x]-1][s[t]-1]))%10007;
15            s[x]+=s[t];
16     }
17     s[x]=s[x]+1; 
18 }
19 int main()
20 {
21     c[0][0]=1;
22     for (int i=1;i<2500;++i)
23     {
24            c[i][0]=c[i][i]=1; 
25            for(int j=1;j<i;++j) c[i][j]=(c[i-1][j-1]+c[i-1][j])%10007;
26     }
27     scanf("%d",&t);
28     for (int i=1;i<=t;i++)
29     {
30            scanf("%d",&n);
31         for (int i=1;i<=n;++i)
32         {
33                scanf("%d",&map[i][0]);
34                for(int j=1;j<=map[i][0];j++) scanf("%d",&map[i][j]);
35         }  
36            dp(1); 
37            printf("%d\n",f[1]);
38     }
39     return 0;
40 }

[樹形dp][組合數] JZOJ P1794 保鏢排隊