把博客园图标替换成自己的图标
把博客园图标替换成自己的图标end

【AT2366】[AGC012F] Prefix Median(DP)

点此看题面

  • 给定一个长度为\(2n-1\)的序列\(a\),你可以将它重新排列。
  • 定义\(b_i\)为前\(2i-1\)个数的中位数,求有多少种可能的\(b\)
  • \(n\le50\)

序列合法的充要条件

首先,考虑\(b_i\)的上下界,必然是从\(a\)中最小的\(2i-1\)个数的中位数到最大的\(2i-1\)个数的中位数。

然后,考虑\(b_i\)的变化值,发现加入两个数最多让中位数移动一位,也就是说,不能有任何之前出现过的数的值在\(b_i\)\(b_{i+1}\)之间。

这是必要条件,同时也是充分的,因为任意一个符号条件的序列我们都能构造出来。

反向考虑

我们从后往前考虑,那么也就是说在之后选中的数之间不能出现新的中位数。

因此设\(f_{i,j,k}\)表示处理完第\(i\)位,在取值下界到\(b_i\)最小值之间有\(j\)种取法,在\(b_i\)最大值到取值上界之间有\(k\)种取法的方案数。

转移的时候枚举一下新选中的\(b_i\)即可。

但注意处理数字重复的情况,即如果新的边界和旧边界值不同我们需要把取法\(j/k\)增加\(1\),相同则不能增加。

代码:\(O(n^4)\)

#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 50
#define X 1000000007
#define Inc(x,y) ((x+=(y))>=X&&(x-=X))
using namespace std;
int n,a[N<<1],f[N+5][N<<1][N<<1];
int main()
{
	RI i,j,k,p;for(scanf("%d",&n),i=1;i<=2*n-1;++i) scanf("%d",a+i);sort(a+1,a+2*n);
	RI x,y;for(f[i=n][0][0]=1;i^1;--i) for(j=0;j<=2*n-1;++j) for(k=0;k<=2*n-1;++k) if(f[i][j][k])//从后往前
	{
		x=j+(a[i-1]!=a[i]),y=k+(a[2*n-i]!=a[2*n-i+1]),Inc(f[i-1][x][y],f[i][j][k]);//边界上数不同则取法加1;不变
		for(p=0;p^x;++p) Inc(f[i-1][p][y+1],f[i][j][k]);for(p=0;p^y;++p) Inc(f[i-1][x+1][p],f[i][j][k]);//左边变化;右边变化
	}
	RI t=0;for(j=0;j<=2*n-1;++j) for(k=0;k<=2*n-1;++k) Inc(t,f[1][j][k]);return printf("%d\n",t),0;//统计答案
}
posted @ 2021-05-26 21:39  TheLostWeak  阅读(79)  评论(0编辑  收藏  举报