【JZOJ5316】【清华集训模拟】merge(DP、括号序)

来源:互联网 发布:软件贵金属喊单室 编辑:程序博客网 时间:2024/06/05 15:18

Description

这里写图片描述

Solution

我们可以想到一个很显然的错误的DP,f[i][j]+=f[i-1][j]+f[i][j-1]
这样明显是会算重的,所以我们要考虑怎样去重。
你可以找一下规律。
我们知道如果在(i,j)前面有一段连续的相同的数的话是会算重的,那么在这之中的转移我们可以强制要求i>=j,那么这个就相当于一个括号序(()()),但是要保证括号影响的连续性,我们需要保证括号外面(…..),如果是从f[i-k][j-k]转移过来的话,那么相当于找2*(k-1)的合法括号序的方案数,那么这个就是卡特兰数。
转移的时候枚举k就好了。枚举k的复杂度最坏情况就是全部相同,枚举次数不会超过n^2。

Code

#include<iostream>#include<stdio.h>#include<string.h>#include<stdio.h>#include<algorithm>#define fo(i,a,b) for(i=a;i<=b;i++)using namespace std;const int maxn=4007,mo=1e9+7;typedef long long ll;ll i,j,k,l,t,n,m,ans;ll f[maxn][maxn],a[maxn],b[maxn],g[maxn][maxn],p[maxn][maxn];ll fact[maxn],ni[maxn];ll qsm(ll x,ll y){    ll z=1;    for(;y;y/=2,x=x*x%mo)if(y&1)z=z*x%mo;    return z;}ll Ca(ll x){    return fact[2*x]*ni[x]%mo*ni[x]%mo*qsm(x+1,mo-2)%mo;}int main(){    freopen("merge.in","r",stdin);    freopen("merge.out","w",stdout);    fact[0]=ni[0]=1;    fo(i,1,maxn-7)fact[i]=fact[i-1]*i%mo,ni[i]=qsm(fact[i],mo-2);    scanf("%lld",&n);fo(i,1,n)scanf("%lld",&a[i]);fo(i,1,n)scanf("%lld",&b[i]),f[0][i]=f[i][0]=1;    f[0][0]=1;    fo(i,1,n){        fo(j,1,n){            if(a[i]==b[j])g[i][j]=g[i-1][j-1]+1;else g[i][j]=0;            fo(k,1,g[i][j])f[i][j]=(f[i][j]-f[i-k][j-k]*Ca(k-1)%mo)%mo;            f[i][j]=(f[i][j]+f[i-1][j]+f[i][j-1])%mo;        }    }    printf("%lld\n",f[n][n]);}