洛谷P3757 [CQOI2017]老C的键盘

传送门

 

首先可以直接把整个序列建成一个完全二叉树的结构,这个应该都看得出来

然后考虑树形dp,以大于为例

设$f[i][j]$表示$i$这个节点在子树中排名第$j$位时的总方案数(因为实际只与相对大小有关,与实际数值无关)

我们考虑如果从当前子树中弄出$k$个节点,其他子树中弄出$j-1$个节点,那么当前节点的大小排名就是$k+j$

然后考虑一下,如果我们不看这个子树,根节点排在第$j$个,方案数是$f[i][j]$,如果只看此子树,此子树的根就是根节点的儿子,它在此子树中的排名可能是$1,2,...k$,那么我们就需要记录一下前缀和

然后考虑合并排列

对于小于根节点的,选出$j-1$个非此子树,对于大于根节点的,选出$sum[x]-1$个非此子树里弄出来的,那么就是一个组合问题了

 1 //minamoto
 2 #include<iostream>
 3 #include<cstdio>
 4 #include<cstring>
 5 #define ll long long
 6 using namespace std;
 7 const int N=105,mod=1e9+7;
 8 int n,tot;char s[N];
 9 ll f[N][N],g[N][N],tmp[N],c[N][N];
10 int head[N],ver[N<<1],Next[N<<1],sum[N];
11 inline void add(int u,int v){
12     ver[++tot]=v,Next[tot]=head[u],head[u]=tot;
13 }
14 void dfs(int x){
15     int l=x<<1,r=l|1;;
16     if(l<=n) add(x,l);
17     if(r<=n) add(x,r);
18     g[x][1]=f[x][1]=sum[x]=1;
19     for(int i=head[x];i;i=Next[i]){
20         int v=ver[i];dfs(v);
21         memset(tmp,0,sizeof(tmp));
22         for(int j=1;j<=sum[x];++j)
23         for(int k=0;k<=sum[v];++k){
24             if(s[v]=='>')
25             tmp[j+k]+=f[x][j]*g[v][k]%mod
26             *c[j+k-1][j-1]%mod*c[sum[x]+sum[v]-j-k][sum[x]-j]%mod;
27             else tmp[j+k]+=f[x][j]*(g[v][sum[v]]-g[v][k]+mod)%mod
28             *c[j+k-1][j-1]%mod*c[sum[x]+sum[v]-j-k][sum[x]-j]%mod;
29         }
30         sum[x]+=sum[v];
31         for(int j=1;j<=sum[x];++j)
32         f[x][j]=tmp[j]%mod,g[x][j]=(g[x][j-1]+f[x][j])%mod;
33     }
34 }
35 int main(){
36 //    freopen("testdata.in","r",stdin);
37     scanf("%d%s",&n,s+2);
38     c[0][0]=1;
39     for(int i=1;i<=n;++i){
40         c[i][0]=1;
41         for(int j=1;j<=i;++j)
42         c[i][j]=(c[i-1][j]+c[i-1][j-1])%mod;
43     }
44     dfs(1);printf("%lld\n",g[1][sum[1]]);
45     return 0;
46 }

 

posted @ 2018-09-13 21:54  bztMinamoto  阅读(183)  评论(0编辑  收藏  举报
Live2D