题意:给你一个长度为2n-1的数组A,设Bi是A的1~2i-1的中位数。问打乱A,有多少种不同的B序列?
标程:
1 #include<bits/stdc++.h> 2 using namespace std; 3 const int mod=998244353; 4 const int N=150; 5 typedef long long ll; 6 int ans,n,a[N],tmp,dp[N][N][N]; 7 void up(int &x,int y) {x=((ll)x+y)%mod;} 8 int main() 9 { 10 scanf("%d",&n); 11 for (int i=1;i<=2*n-1;i++) scanf("%d",&a[i]); 12 sort(a+1,a+2*n); 13 dp[n][a[n]!=a[n-1]][a[n]!=a[n+1]]=1; 14 for (int i=n;i>1;i--) 15 { 16 int x=(a[i-1]!=a[i-2]),y=(a[2*n-i+1]!=a[2*n-i+2]); 17 for (int l=0;l<=(n-i+1)*2;l++) 18 for (int r=0;r<=(n-i+1)*2;r++) 19 if (tmp=dp[i][l][r]) 20 { 21 up(dp[i-1][l+x][r+y],tmp); 22 for (int _l=0;_l<l;_l++) up(dp[i-1][_l+x][r+y+1],tmp); 23 for (int _r=0;_r<r;_r++) up(dp[i-1][l+x+1][_r+y],tmp); 24 } 25 } 26 for (int i=0;i<=2*n-1;i++) 27 for (int j=0;j<=2*n-1;j++) up(ans,dp[1][i][j]); 28 printf("%d ",ans); 29 return 0; 30 }
题解:技巧dp
出题人非常良心,当ai<=2时,答案是2^min(1的个数,2的个数)。
最后一个中位数是可以直接确定的,由于中位数在序列中最多左右按照数值移动相邻一位因此可以用倒三角表示出每一位中位数的可能集合。
1 2 3 4 5 6 7
2 3 4 5 6
3 4 5
4
直接在该三角形上dp,dp[i][j][k]表示第i层,j表示在该层的候选中位数中,该次选取的中位数的左边有几种数(去重)可取,k表示右边。tmp=dp[i][j][k]。
左边取掉一列,右边取掉一列,中位数不变:dp[i][j+扩展][k+扩展]+=tmp。
左边取掉两列,走到上一层中位数应该右移:枚举取到的是哪个中位数,如果能够跳过去,说明中间的几列在之前都没有出现,之前的中位数集合都是不能取它们的,dp[i][j+扩展+1(1表示中间一列变为可取)][r(枚举是哪个中位数,r为该中位数右边还有几个数)+扩展]+=tmp。
右边取两列同理。