有一个长度为 n n n的序列 A A A,你可以将 A A A中的元素进行重排得到一个新的序列 B B B,规则如下:
操作的顺序是任意的,问总共可能得到多少种不同的 B B B,输出答案模 1 0 9 + 7 10^9+7 109+7后的值。
1 ≤ n ≤ 100 , 1 ≤ A i ≤ n 1\leq n\leq 100,1\leq A_i\leq n 1≤n≤100,1≤Ai≤n
注意到 1 ≤ a i ≤ n 1\leq a_i\leq n 1≤ai≤n,我们先考虑如果 A i A_i Ai是 1 1 1到 n n n的一个排列该怎么求解。
我们设 n n n在 A A A序列和 B B B序列中出现的位置分别为 p , q p,q p,q,即 A p = B q = n A_p=B_q=n Ap=Bq=n。因为 T T T是小根堆,且 A A A是 1 1 1到 n n n的一个排列,所以当拿出 n n n时就意味着 T T T已经被搬空了。由此可得, B B B的前 q q q个元素就是 A A A的前 q q q个元素,只是顺序可能不太一样。所以, { A 1 , A 2 , … , A q } = { B 1 , B 2 , … , B q } \{A_1,A_2,\dots,A_q\}=\{B_1,B_2,\dots,B_q\} {A1,A2,…,Aq}={B1,B2,…,Bq},且 { A q + 1 , A q + 2 , … , A n } = { B q + 1 , B q + 2 , … , B n } \{A_{q+1},A_{q+2},\dots,A_n\}=\{B_{q+1},B_{q+2},\dots,B_n\} {Aq+1,Aq+2,…,An}={Bq+1,Bq+2,…,Bn}。
既然我们已经知道 B q = n B_q=n Bq=n,那么我们可以把原问题看作两个子问题: [ 1 , q − 1 ] [1,q-1] [1,q−1]和 [ q + 1 , n ] [q+1,n] [q+1,n]。其中 B 1 , B 2 , … , B q − 1 B_1,B_2,\dots,B_{q-1} B1,B2,…,Bq−1是由 A 1 , A 2 , … , A q A_1,A_2,\dots,A_q A1,A2,…,Aq去掉 A p A_p Ap再经过一些操作变化而来的,而 B q + 1 , B q + 2 , … , B n B_{q+1},B_{q+2},\dots,B_n Bq+1,Bq+2,…,Bn是由 A q + 1 , A q + 2 , … , A n A_{q+1},A_{q+2},\dots,A_n Aq+1,Aq+2,…,An经过一些操作变化而来的。
我们考虑递归地解决子问题。
因为在每次将一个问题分为子问题时都是将一个最大值删去,再分成两个部分,那么每个子问题所对应的子序列 A ′ A' A′都是原序列 A A A的某一段区间 [ l , r ] [l,r] [l,r]删去一些较大的数而得到的。
我们考虑 D P DP DP,设 f l , r , i f_{l,r,i} fl,r,i表示 A l , A l + 1 , … , A r A_l,A_{l+1},\dots,A_r Al,Al+1,…,Ar中不超过 i i i的数构成的子序列的方案数。设 A m = i A_m=i Am=i,下面考虑转移:
那我们再来考虑如何处理 A A A不是 1 1 1到 n n n的排列的情况。
我们约定,当小根堆的堆顶为 i i i,并且堆中存在多个 i i i,那么我们取的时候先取下标最小的。
那在我们的约定下,可以将原数列转化为一个排列。
考虑这样转化的正确性。转化后 A A A中的数和 B B B中的数一一对应,而且 B B B中相同的数的先后顺序和在 A A A中的先后顺序是相同的,也就是说先后顺序一定,所以这样转化是可行的。
时间复杂度为 O ( n 4 ) O(n^4) O(n4),这跑不满,是可以过的。
#include
using namespace std;
const int N=100;
const long long mod=1e9+7;
int n,a[N+5],b[N+5],cnt[N+5],rk[N+5];
long long f[N+5][N+5][N+5];
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);++cnt[a[i]];
}
for(int i=1;i<=n;i++) cnt[i]+=cnt[i-1];
for(int i=n;i>=1;i--){
b[i]=cnt[a[i]]--;rk[b[i]]=i;
}
for(int i=1;i<=n+1;i++){
for(int j=0;j<=n+1;j++) f[i][i-1][j]=1;
}
for(int l=n;l>=1;l--){
for(int r=l;r<=n;r++){
f[l][r][0]=1;
if(b[l]<b[r])
for(int i=1;i<b[r];i++) f[l][r][i]=f[l][r-1][i];
else
for(int i=1;i<b[l];i++) f[l][r][i]=f[l+1][r][i];
for(int i=max(b[l],b[r]);i<=n;i++){
if(rk[i]<l||rk[i]>r) f[l][r][i]=f[l][r][i-1];
else{
for(int j=rk[i];j<=r;j++){
if(b[j]<=i)
f[l][r][i]=(f[l][r][i]+f[l][j][i-1]*f[j+1][r][i-1])%mod;
}
}
}
}
}
printf("%lld",f[1][n][n]);
return 0;
}