束缚二叉树 [树形dp]

束缚二叉树



\color{red}{正解部分}

一个子树内的 前序遍历序 是连续的,
于是可以设 F[i,j]F[i, j] 表示以 ii 的子树, 子树末节点编号为 jj 时的方案数,
:F[i,j]=k=ijF[i,k]×F[k+1,j]状态转移: F[i, j] = \sum\limits_{k=i}^{j} F[i, k] \times F[k+1, j] .

考虑怎么满足题目的限制条件, 设左子树的 中序遍历 编号为 aa, 右子树为 cc, 根节点为 bb,
中序遍历 的要求是: a<ba < b, a<ca < c, b<cb < c .

由于 前序遍历 编号是连续的, 所以可以考虑使用 二维矩阵前缀和 判断 转移是否合法 .

具体来说:
若存在限制 u,vu, v, 则令矩阵数组 a[u,v]=1a[u, v] = 1, 表示 中序遍历uu 的编号 要 小于 vv 的 编号,
于是对于前面提到的三个限制, 只需要

  • 判断 左上角 (i,i+1)(i, i+1) 右下角 (i,k)(i, k) 的矩阵是否含有 11 .
  • 判断 左上角 (k+1,i+1)(k+1, i+1) 右下角 (j,k)(j, k) 的矩阵是否含有 11 .
  • 判断 左上角 (k+1,i)(k+1, i) 右下角 (j,i)(j, i) 的矩阵是否含有 11 .

若上方有一个矩阵含有 11, 说明状态转移不合法, 不予以转移 .


\color{red}{实现部分}

使用递归实现 .

#include<bits/stdc++.h>
#define reg register

int read(){
        char c;
        int s = 0, flag = 1;
        while((c=getchar()) && !isdigit(c))
                if(c == '-'){ flag = -1, c = getchar(); break ; }
        while(isdigit(c)) s = s*10 + c-'0', c = getchar();
        return s * flag;
}

const int maxn = 305;
const int mod = 1e9 + 7;

int N;
int M;
int a[maxn][maxn];
int F[maxn][maxn];

int calc(int x1, int y1, int x2, int y2){ return a[x2][y2] - a[x2][y1-1] - a[x1-1][y2] + a[x1-1][y1-1]; }

int solve(int i, int j){
        if(i >= j) return 1;
        if(~F[i][j]) return F[i][j];
        int res = 0;
        for(reg int k = i; k <= j; k ++){
                if(calc(i, i+1, i, k)) continue ;
                if(calc(k+1, i, j, i)) continue ;
                if(calc(k+1, i+1, j, k)) continue ;
                res = (res + (1ll*solve(i+1, k)*solve(k+1, j)%mod)) % mod;
        }
        return F[i][j] = res;
}

void Work(){
        N = read(), M = read();
        memset(a, 0, sizeof a);
        memset(F, -1, sizeof F);
        for(reg int i = 1; i <= M; i ++){
                int u = read(), v = read();
                if(u == v){ printf("0\n"); return ; }
                a[u][v] = 1;
        }
        for(reg int i = 1; i <= N; i ++)
                for(reg int j = 1; j <= N; j ++) a[i][j] += a[i-1][j] + a[i][j-1] - a[i-1][j-1];
        printf("%d\n", solve(1, N));
}

int main(){
        int T = read();
        while(T --) Work();
        return 0;
}
posted @ 2019-09-29 21:33  XXX_Zbr  阅读(162)  评论(0编辑  收藏  举报