• sss


    <更新提示>

    <第一次更新>


    <正文>

    count

    Description

    既然是萌萌哒 visit_world 的比赛,那必然会有一道计数题啦!

    考虑一个N个节点的二叉树,它的节点被标上了1-N的编号. 并且,编号为i的节点在二叉树的前序遍历中恰好是第i个出现.

    我们定义Ai表示编号为i的点在二叉树的中序遍历中出现的位置.

    现在,给出M个限制条件,第i个限制条件给出了ui,vi,表示 Aui<Avi,也即中序遍历中ui在vi之前出现.

    你需要计算有多少种不同的带标号二叉树满足上述全部限制条件,答案对(10^9+7)取模.

    Input Format

    第一行一个整数T(1≤T≤5), 表示数据组数.

    每组数据第一行为两个整数N,M,意义如题目所述.

    接下来M行,每行两个整数ui,vi(1≤ui,vi≤N) , 描述一条限制。

    Output Format

    对每组数据,输出一行一个整数,表示答案.

    Sample Input

    3
    5 0
    3 2
    1 2
    2 3
    3 3
    1 2
    2 3
    3 1
    

    Sample Output

    42
    1
    0
    

    解析

    我们知道一个前序遍历和一个中序遍历可以唯一确定一棵二叉树,也就是让我们求前序遍历在满足(m)个限定要求下可以对应多少个中序遍历。

    考虑区间(dp)(f[l][r])代表前序遍历的区间为([l,r])(l)为根节点的子树中对应的方案数。

    如果没有限制,直接考虑划分子树的方式划分区间,转移累加方案数即可。有限制时,也就是有些转移不能执行了,我们可以把限制打在一个二维数组里,求一下二维前缀和实现矩阵求和。这样转移时的限制查询就可以变成某个矩阵的求和了,若有值就说明存在若干限制,不能转移。

    (Code:)

    #include <bits/stdc++.h>
    using namespace std;
    const int N = 420 , Mod = 1e9+7;
    int n,m,s[N][N];
    long long f[N][N];
    inline void input(void)
    {
        scanf("%d%d",&n,&m);
        for (int i=1;i<=m;i++)
        {
            int x,y;
            scanf("%d%d",&x,&y);
            s[x][y] ++;
        }
    }
    inline int calc(int x1,int y1,int x2,int y2) { return s[x2][y2] - s[x1-1][y2] - s[x2][y1-1] + s[x1-1][y1-1]; }
    inline void init(void)
    {
        for (int i=1;i<=n;i++)
            for (int j=1;j<=n;j++)
                s[i][j] += s[i][j-1];
        for (int i=1;i<=n;i++)
            for (int j=1;j<=n;j++)
                s[i][j] += s[i-1][j];
    }
    inline void add(long long &a,long long b) { a += b; if ( a >= Mod ) a -= Mod; }
    inline void dp(void)
    {
        for (int i=1;i<=n;i++) f[i][i] = 1;
        for (int len=2;len<=n;len++)
            for (int l=1,r;(r=l+len-1)<=n;l++)
            {
                if ( calc( l , l+1 , l , r ) == 0 )
                    add( f[l][r] , f[l+1][r] );
                if ( calc( l+1 , l , r , l ) == 0 )
                    add( f[l][r] , f[l+1][r] );
                for (int k=l+1;k<=r-1;k++)
                    if ( calc( l , l+1 , l , k ) == 0 && calc( k+1 , l , r , k ) == 0 )
                        add( f[l][r] , 1LL * f[l+1][k] * f[k+1][r] % Mod );
            }
    }
    int main(void)
    {
        freopen("input.in","r",stdin);
        freopen("output.out","w",stdout);
        int T;
        scanf("%d",&T);
        while ( T --> 0 )
        {
            memset( f , 0 , sizeof f );
            memset( s , 0 , sizeof s );
            input();
            init();
            dp();
            printf("%lld
    ",f[1][n]);
        }
        return 0;
    }
    
    

    <后记>

  • 相关阅读:
    elasticsearch head插件安装
    ELK部署配置使用记录
    windows 安装mysql
    vs2017创建dotnetcore web项目,并部署到centos7上
    CentOS 7 安装jdk
    CentOS 7 配置网络
    Surging 记录
    记录一下地址
    net core 依懒注入 中间件
    Elasticsearch 配置文件
  • 原文地址:https://www.cnblogs.com/Parsnip/p/11415075.html
Copyright © 2020-2023  润新知