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]);
}