沉玉谷

题目

每次可以删除颜色相同的连续段。

删除的次数、顺序不同,答案就不一样。

因此题目即为求合法的删除方案数。

看数据范围,\(N\)\(50\)!因此我们需要 \(O(N^{4\sim 6})\) 时间复杂度的算法。

这么高时间复杂度,本蒟蒻只能想到 DP。

区间 DP

\(dp_{len,l,k}\) 表示区间 \([l,l+len-1]\) 删除 \(k\) 次删完的方案数,考虑转移。

\(r=l+len-1\)

转移得到 \(dp_{len,l,k}\) 可以这样想:

由区间 \([l,r-1]\) 后加一位 \(a_{r}\) 得到区间 \([l,r]\) 的答案。

  1. 分别删除区间 \([l,r-1]\)\(a_r\)
  2. \(a_r\)\(a_j(l\leq j\leq r-1,a_j=a_r)\) 一起合并。

第二种转移,我们需要 \(a_j,a_r\) 同一次删除,且区间 \([j+1,r-1]\) 必须在 \(a_j,a_r\) 被删掉之前先删掉,因此答案由区间 \([l,j]\) 和区间 \([j+1,r-1]\) 转移得到,因为我们只要 \(a_j\) 在第 \(k\) 次删除的方案,为了方便,我们将 DP 加一维 \(x\)\(dp_{len,l,k,x}\) 表示区间 \([l,l+len-1]\) 删除 \(k\) 次删完且 \(a_r\) 刚好在第 \(x\) 次被删掉的方案数。

详细 DP 转移过程如下:

转移 \(dp_{len,l,k,x}\) 时,

  1. 初始化 \(dp_{len,l,k=1,x=1}\) 的值(为 \(0\)\(1\)),判断区间 \([l,r]\) 是否能整块一次删掉。

  2. 如果区间 \([l,r-1]\ k-1\) 次删完是合法的,加上 \(dp_{len-1,l,k-1,1\leq x \leq k-1}\) 的答案。

  3. 对于第二种转移方式,若 \(j=r-1\)\(a_r\)\(a_j\) 被删除的那一次可以直接被一起删掉,加上 \(dp_{len-1,l,k,x}\) 的答案。
    否则枚举区间 \(1\)\([j+1,r-1]\) 或区间 \(2\)\([l,j]\) 要删除几次删完。然后就可以算出 \(a_j\) 在删除区间 \([l,j]\) 的步骤中是在第几次被删除的(\(x_1\)),加上答案 \(dp_{len_1,l,k_1,x_1}\times dp_{len_2,l_2,k2}\times calc(x_1,k_2)\)

其中 \(calc(x_1,k_2)=(x_1-1+k_2)!\div ((x_1-1)!\times k_2!)\)。计算区间 \(2\)\(k_2\) 个删除步骤必须都在区间 \(1\) 的第 \(x_1\) 步骤前的方案数。想象为 \(k_2+(x_1-1)\) 个点排序,由于 \(k_2\) 个步骤和 \(x_1-1\) 个步骤的前后顺序分别是一定的,要除去 \(k_2\) 个点和 \(x_1-1\) 个点分别的排列数量。

最后答案为区间 \([1,n]\) 删除任意次删完且 \(a_n\) 在任意次被删掉的方案数。

时间复杂度 \(O(n^6)\),(DP 状态 \(O(n^4)\),转移 \(O(n^2)\)),由于常数很小,不会超时。

Code

//100pts

#include<bits/stdc++.h>
#define sf scanf
#define pf printf
#define il inline
using namespace std;
typedef long long ll;
const int N=51,mod=1e9+7;

int n,a[N];
int dp[N][N][N][N];
int f[N][N][N];
int jc[N];
int ny[N];

void add(int &a,int b){
	a=(1ll*a+1ll*b)%mod;
}
int ksm(int a,int b){
	int sum=1;
	while(b){
		if(b&1) sum=1ll*sum*a%mod;
		a=1ll*a*a%mod;
		b>>=1;
	}
	return sum;
}
int calc(int a,int b,int c){
	return 1ll*jc[a]*ny[b]%mod*ny[c]%mod;
}

int main(){
//	freopen("jade7.in","r",stdin);
//	freopen("jade.out","w",stdout);
	sf("%d",&n);
	jc[0]=1;
	ny[0]=1;
	for(int i=1;i<=n;i++){
		jc[i]=1ll*jc[i-1]*i%mod;
		ny[i]=ksm(jc[i],mod-2);
	}
	for(int i=1;i<=n;i++){
		sf("%d",&a[i]);
	}
	for(int len=1;len<=n;len++){
		for(int l=1,r=l+len-1;r<=n;l++,r++){
  			dp[len][l][1][1]=1;
			for(int i=l+1;i<=r;i++){
				if(a[i]!=a[i-1]) {
					dp[len][l][1][1]=0;
					break;
				}
			}
			f[len][l][1]=dp[len][l][1][1];
//			pf("  %d %d 1 1 : %d\n",len,l,dp[len][l][1][1]);
			for(int k=2;k<=len;k++){
				for(int x=1;x<=k;x++){
					if(dp[len-1][l][k-1][k-1]){
						add(dp[len][l][k][x],f[len-1][l][k-1]);
//						pf("   add1: %d %d %d %d\n",len-1,l,k-1,f[len-1][l][k-1]);
					}
					for(int i=l;i<=r-1;i++){
						if(a[i]!=a[r]) continue;
						if(i==r-1){
							add(dp[len][l][k][x],dp[len-1][l][k][x]);
//							pf("   add2: %d %d %d %d %d\n",len-1,l,k,x,dp[len-1][l][k][x]);
							continue;
						}
						for(int k2=1;k2<k&&k2<x;k2++){
							int k1=k-k2,x1=x-k2;
							add(dp[len][l][k][x],1ll*dp[i-l+1][l][k1][x1]*f[r-1-(i+1)+1][i+1][k2]%mod*calc(x1-1+k2,x1-1,k2)%mod);
//							pf("   add3: %d %d %d %d %d  %d %d %d %d  %d %d %d\n",i-l+1,l,k1,x1,dp[i-l+1][l][k1][x1],r-1-(i+1)+1,i+1,k2,f[r-1-(i+1)+1][i+1][k2],x1,k2,calc(x1+k2,x1-1,k2));
						}
					}
					add(f[len][l][k],dp[len][l][k][x]);
//					pf("  %d %d %d %d : %d\n",len,l,k,x,dp[len][l][k][x]);
				}
				
			}
		}
	}
	int ans=0;
	for(int i=1;i<=n;i++){
		add(ans,f[n][1][i]);
	}
	pf("%d\n",ans);
}
posted @ 2024-08-13 21:23  liyixin  阅读(11)  评论(0编辑  收藏  举报