[AGC012F]Prefix Median
题目大意:
给定一个长度为$2n-1(n\le50)$的数组$a$,可以重排$a$中的元素,生成一个长度为$n$的数组$b$,其中$b_i$为$a_1\sim a_{2i-1}$的中位数。求对于给定的$a$能生成多少种不同的$b$。
思路:
对$a$进行排序,转化题意。求满足以下3个条件的长度为$n$的数列$b$的个数:
1.$b_i\in\{a_i,a_{i+1},\ldots,a_{2n-i}\}$;
2.对于$(i<j)$,不存在$b_i<b_j<b_{i+1}$;
3.对于$(i<j)$,不存在$b_i>b_j>b_{i+1}$。
用$f[i][j][k]$表示考虑$b$的第$i$位,比它小的可选数有$j$种,比它大的可选数有$k$种。即可用动态规划求得。
每次转移设$l=[a_i\ne a_{i-1}],r=[a_{m-i+1}\ne a_{m-i+2}]$,对应条件1,表示当前转移可以新填的数。若$a_i=a_{i-1}$或$a_{m-i+1}=a_{m-i+2}$则说明不会增加新填的数。
转移1:$f[i-1][j+l][k+r]+=f[i][j][k]$,即当前填的数还是上次的数,但是两边各多出$l$或$r$个可以填。
转移2:$f[i-1][t][k+r+1]+=f[i][j][k](t<j+l)$,表示若将当前填的数变小,左边还剩$t$个可以填,这里本来填的变成了右边的。
转移3:$f[i-1][j+l+1][t]+=f[i][j][k](t<k+r)$,表示若将当前填的数变大,右边还剩$t$个可以填,这里本来填的变成了左边的。
状态$O(n^3)$,转移$O(n)$,时间复杂度$O(n^4)$。
1 #include<cstdio> 2 #include<cctype> 3 #include<algorithm> 4 inline int getint() { 5 register char ch; 6 while(!isdigit(ch=getchar())); 7 register int x=ch^'0'; 8 while(isdigit(ch=getchar())) x=(((x<<2)+x)<<1)+(ch^'0'); 9 return x; 10 } 11 constexpr int mod=1e9+7; 12 constexpr int N=51,M=100; 13 int a[N],f[N][M][M]; 14 int main() { 15 const int n=getint(),m=n*2-1; 16 for(register int i=1;i<=m;i++) a[i]=getint(); 17 std::sort(&a[1],&a[m]+1); 18 f[n][0][0]=1; 19 for(register int i=n;i>1;i--) { 20 const bool l=a[i]!=a[i-1],r=a[m-i+1]!=a[m-i+2]; 21 for(register int j=0;j<=m;j++) { 22 for(register int k=0;k<=m;k++) { 23 if(!f[i][j][k]) continue; 24 (f[i-1][j+l][k+r]+=f[i][j][k])%=mod; 25 for(register int t=0;t<j+l;t++) { 26 (f[i-1][t][k+r+1]+=f[i][j][k])%=mod; 27 } 28 for(register int t=0;t<k+r;t++) { 29 (f[i-1][j+l+1][t]+=f[i][j][k])%=mod; 30 } 31 } 32 } 33 } 34 int ans=0; 35 for(register int i=0;i<=m;i++) { 36 for(register int j=0;j<=m;j++) { 37 (ans+=f[1][i][j])%=mod; 38 } 39 } 40 printf("%d\n",ans); 41 return 0; 42 }