[樹形dp][組合數] JZOJ P1794 保鏢排隊
阿新 • • 發佈:2018-02-07
scrip 方案 sca 原理 數據 clear 排列 排隊 ios
題解
-
首先要明確的是,屬於不同子樹的點可以混在一起,只要同一子樹的有序就可以了。
-
如果一個結點有兩個兒子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 保鏢排隊