zoukankan      html  css  js  c++  java
  • 计数(count)

    【from new_dtoj 3967: 计数(count)】
    题目描述
    考虑一个 N个节点的二叉树,它的节点被标上了1∼N 的编号. 并且,编号为i的节点在二叉树的前序遍历中恰好是第i个出现.
    我们定义Ai 表示编号为i的点在二叉树的中序遍历中出现的位置.
    现在,给出M个限制条件,第i个限制条件给出了ui,vi,表示Aui<Avi ,也即中序遍历中ui在vi 之前出现.
    你需要计算有多少种不同的带标号二叉树满足上述全部限制条件,答案对109{10^9}+7 取模.
    输入
    第一行一个整数T(1≤T≤5), 表示数据组数.
    每组数据第一行为两个整数N,M,意义如题目所述.
    接下来M 行,每行两个整数ui,vi(1≤ui,vi≤N) . 描述一条限制.
    输出
    对每组数据,输出一行一个整数,表示答案.
    样例输入
    3
    5 0
    3 2
    1 2
    2 3
    3 3
    1 2
    2 3
    3 1
    样例输出
    42
    1
    0
    提示
    解释
    第一组数据,无任何限制时,5 个点的二叉树形态个数为42 .
    第二组数据,唯一满足条件的二叉树的形态为 1 - 2 - 3.(1 为根,2, 3 均为右儿子).
    第三组数据,限制条件产生矛盾.
    数据范围和子任务
    对于全部的测试数据,保证T≤5,N≤400,M≤103{10^3} .
    子任务 1(20 分):M=0 .
    子任务 2(15 分):N≤10 .
    子任务 3(20 分):N≤50,M≤1 .
    子任务 4(15 分):N≤50 .
    子任务 5(30 分):无特殊限制.
    题解:
    由于前序遍历是1-n,所以可以发现每一棵子树的dfs序是连续的,并且它的根是这棵子树内编号最小的点
    所以设f[i][j]表示以i为一棵子树的根,这棵子树最大编号为j,能满足限制的答案值
    明显若不考虑限制,f[i][j]=k=ijF(i+1,k)F(k+1,j)f[i][j]=sum_{k=i}^{j}F(i+1,k)*F(k+1,j)
    考虑限制,我们把一个限制看成二维平面上的一个点(ui,vi),并且这个点的权值为1
    那么当在顶点坐标为(k+1,i+1),(j,k);(i,i+1),(i,k);(k+1,i),(j,i)(左上和右下)内的和为0时,k是满足限制的,所以就完美解决啦
    代码如下

    #include <cstdio>
    using namespace std;
    const int P=1e9+7;
    int T,n,m,f[405][405],s[405][405];
    bool calm(int l1,int r1,int l2,int r2){
        if (r1<l1||r2<l2) return 1;
        return s[r1][r2]-s[l1-1][r2]-s[r1][l2-1]+s[l1-1][l2-1]==0;
    }
    bool judge(int l1,int r1,int l2,int r2){
        return calm(l1,r1,l2,r2)&calm(l2-1,l2-1,l2,r2)&calm(l1,r1,l2-1,l2-1);
    }
    int F(int l,int r){
        if (l>=r) return f[l][r]=1;
        if (~f[l][r]) return f[l][r];f[l][r]=0;
        for (int i=l;i<=r;i++) if (judge(i+1,r,l+1,i))
            f[l][r]=(1ll*f[l][r]+1ll*F(l+1,i)*F(i+1,r)%P)%P;
        return f[l][r];
    }
    int main(){
        for (scanf("%d",&T);T--;){
            scanf("%d%d",&n,&m);
            for (int i=1;i<=n;i++) for (int j=1;j<=n;j++) s[i][j]=0;
            for (int x,y,i=1;i<=m;i++) scanf("%d%d",&x,&y),s[x][y]=1;
            for (int i=1;i<=n;i++) for (int j=1;j<=n;j++)
                s[i][j]+=s[i-1][j]+s[i][j-1]-s[i-1][j-1],f[i][j]=-1;
            printf("%d
    ",F(1,n));
        }
        return 0;
    }
    
  • 相关阅读:
    二分查找binarySearch
    快速排序quicksort实现方法
    读书清单
    windows 下遍历文件夹
    如何输出 android.mk 及 Application.mk 中个变量的值
    【转】 armeabi与armeabi-v7a
    Application.mk
    【转】TypeError: 'module' object is not callable 原因分析
    User breakpoint called from code at XXX的解决方式记录
    关于溢出的一些体会
  • 原文地址:https://www.cnblogs.com/xjqxjq/p/10544731.html
Copyright © 2011-2022 走看看