[hdu5181]numbers

来源:互联网 发布:im短域名 编辑:程序博客网 时间:2024/05/21 09:52

题目大意

把1~n顺序入栈,你可以决定出栈序列。
有m组关系,每组关系限制j要在k之前出栈。

DP

首先有个坑点请先判掉,就是存在限制j=k就输出0。。
我们考虑入栈出栈序列。
1入栈,写一个1。
2入栈,写一个2。
3入栈,写一个3。
3出栈,写一个3。
类似这样可以写出一个进出栈序列。
如果我们在最前和最后都加1个0,还可以看做是一个括号序列,也就是一颗以0为根节点的树。
这颗树有以下几个特点:
1、若节点i的子树大小为k,则子树i中的点的编号是[i,i+k-1]。
2、如果i是j的祖先,说明j比i先出栈。
这样的话限制可以转化成子树大小的上下界限制。
我们设f[i,j]表示i子树大小为j,且每一个点都满足限制的方案数。
暂时先不考虑i的限制。
初始f[i,1]=1。
那么有
f[i,j]=j1k=1f[i,jk]f[i+jk][k]
做完这个dp后,我们把非法的子树大小j都来个f[i,j]=0。
最后输出f[0,n+1]即可。

#include<cstdio>#include<algorithm>#define fo(i,a,b) for(i=a;i<=b;i++)#define fd(i,a,b) for(i=a;i>=b;i--)using namespace std;typedef long long ll;const int maxn=600+10,mo=1000000007;int f[maxn][maxn],L[maxn],R[maxn];int i,j,k,l,t,n,m,ca;bool czy;int main(){    scanf("%d",&ca);    while (ca--){        scanf("%d%d",&n,&m);        czy=0;        fo(i,0,n) L[i]=0,R[i]=n+1;        fo(i,1,m){            scanf("%d%d",&j,&k);            if (j==k) czy=1;            if (j<k) R[j]=min(R[j],k-j);            else L[k]=max(L[k],j-k+1);        }        if (czy){            printf("0\n");            continue;        }        fd(i,n,0){            fo(j,1,n+1) f[i][j]=0;            f[i][1]=1;            fo(j,2,n-i+1)                fo(k,1,j-1)                    f[i][j]=(f[i][j]+(ll)f[i][j-k]*f[i+j-k][k]%mo)%mo;            fo(j,1,L[i]-1) f[i][j]=0;            fo(j,R[i]+1,n+1) f[i][j]=0;        }        printf("%d\n",f[0][n+1]);    }}