4824: [Cqoi2017]老C的键盘

44 篇文章 0 订阅
16 篇文章 0 订阅

把每个不等关系看做一条边

这样原串就变成一棵树

考虑以i为根的子树,f[i][j]为点i在子树中排名为j的方案数

枚举j,枚举比i小的数中有多少个节点是左儿子

对f数组统计前缀后缀和,乘上组合数转移即可

#include<iostream>
#include<cstdio>
#include<cstring>
#define min(a,b) ((a) < (b) ? (a) : (b))
using namespace std;
 
const int N = 105;
typedef long long LL;
const LL mo = 1000000007;
 
int n,C[N][N],f[N][N],sp[N][N],sf[N][N],siz[N];
char s[N];
 
inline int Mul(const LL &x,const LL &y) {return x * y % mo;}
inline int Add(const int &x,const int &y) {return x + y < mo ? x + y : x + y - mo;}
 
inline void Dfs(int x)
{
    siz[x] = 1;
    int lc = x << 1,rc = x << 1 | 1;
    if (lc > n && rc > n) f[x][1] = 1;
    else if (lc <= n && rc > n)
    {
        Dfs(lc); siz[x] += siz[lc];
        f[x][s[lc] == '>' ? 2 : 1] = 1;
    }
    else
    {
        Dfs(lc); siz[x] += siz[lc];
        Dfs(rc); siz[x] += siz[rc];
        for (int i = 1; i <= siz[x]; i++)
            for (int j = 0; j < min(i,siz[lc] + 1); j++)
            {
                int k = i - 1 - j;
                int tmp = Mul(C[i - 1][j],C[siz[x] - i][siz[lc] - j]);
                if (s[lc] == '>') tmp = Mul(tmp,sp[lc][j]);
                else tmp = Mul(tmp,sf[lc][j + 1]);
                if (s[rc] == '>') tmp = Mul(tmp,sp[rc][k]);
                else tmp = Mul(tmp,sf[rc][k + 1]);
                f[x][i] = Add(f[x][i],tmp);
            }
    }
    for (int i = n; i; i--) sf[x][i] = Add(sf[x][i + 1],f[x][i]);
    for (int i = 1; i <= siz[x]; i++) sp[x][i] = Add(sp[x][i - 1],f[x][i]);
}
 
int main()
{
    #ifdef DMC
        freopen("DMC.txt","r",stdin);
    #endif
     
    cin >> n; scanf("%s",s + 2); C[0][0] = 1;
    for (int i = 1; i <= n; i++)
    {
        C[i][0] = 1;
        for (int j = 1; j <= i; j++)
            C[i][j] = Add(C[i - 1][j],C[i - 1][j - 1]);
    }
    Dfs(1); cout << sf[1][1] << endl;
    return 0;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值