【AT2366】[AGC012F] Prefix Median(DP)

点此看题面

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

序列合法的充要条件

首先,考虑(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
",t),0;//统计答案
}
败得义无反顾,弱得一无是处
原文地址:https://www.cnblogs.com/chenxiaoran666/p/AT2366.html