LOJ#3023 老C的键盘
给定树,每条边有个大于号或者小于号,表示两个节点编号的大小关系。问有多少种树满足条件。n <= 100
解:树形DP。
设fij表示以i为根的子树中i是第j小的。转移的时候要乘上两个组合数。
1 #include <bits/stdc++.h> 2 3 const int N = 110, MO = 1000000007; 4 5 struct Edge { 6 int nex, v, len; /// 0 fa<son 1 fa>son 7 }edge[N << 1]; int tp; 8 9 int f[N][N], e[N], n, siz[N], temp[N], C[N][N]; 10 char str[N]; 11 12 inline void add(int x, int y, int z) { 13 tp++; 14 edge[tp].v = y; 15 edge[tp].len = z; 16 edge[tp].nex = e[x]; 17 e[x] = tp; 18 return; 19 } 20 21 void DFS(int x) { 22 siz[x] = 1; 23 f[x][1] = 1; 24 for(int i = e[x]; i; i = edge[i].nex) { 25 int y = edge[i].v; 26 //printf("%d -> %d \n", x, y); 27 DFS(y); 28 /// DP 29 memcpy(temp, f[x], sizeof(f[x])); 30 memset(f[x], 0, sizeof(f[x])); 31 for(int j = 1; j <= siz[x]; j++) { 32 /// temp[j] 33 if(!temp[j]) continue; 34 for(int k = 1; k <= siz[y]; k++) { 35 //if(y == 5) printf("len = %d \n", edge[i].len); 36 if(edge[i].len) { /// fa > son 37 for(int p = j + k; p <= j + siz[y]; p++) { 38 f[x][p] = (f[x][p] + 1ll * temp[j] * f[y][k] % MO * C[p - 1][j - 1] % MO * C[siz[x] + siz[y] - p][siz[x] - j] % MO) % MO; 39 //printf("f %d %d = %d \n", x, p, f[x][p]); 40 } 41 } 42 else { /// fa < son 43 for(int p = j; p <= j + k - 1; p++) { 44 f[x][p] = (f[x][p] + 1ll * temp[j] * f[y][k] % MO * C[p - 1][j - 1] % MO * C[siz[x] + siz[y] - p][siz[x] - j] % MO) % MO; 45 //printf("f %d %d = %d \n", x, p, f[x][p]); 46 } 47 } 48 } 49 } 50 siz[x] += siz[y]; 51 } 52 return; 53 } 54 55 int main() { 56 scanf("%d", &n); 57 for(int i = 0; i <= n; i++) { 58 C[i][0] = C[i][i] = 1; 59 for(int j = 1; j < i; j++) { 60 C[i][j] = (C[i - 1][j] + C[i - 1][j - 1]) % MO; 61 } 62 } 63 scanf("%s", str); 64 for(int i = 2; i <= n; i++) { 65 if(str[i - 2] == '<') { 66 add(i / 2, i, 0); 67 } 68 else { 69 add(i / 2, i, 1); 70 } 71 } 72 73 DFS(1); 74 75 int ans = 0; 76 for(int i = 1; i <= n; i++) { 77 ans = (ans + f[1][i]) % MO; 78 } 79 printf("%d\n", ans); 80 return 0; 81 }