zoukankan      html  css  js  c++  java
  • 【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;//统计答案
    }
    
    败得义无反顾,弱得一无是处
  • 相关阅读:
    Service、chkconfig命令
    mongoDB 入门
    HTTP 缓存
    MIME类型记录
    CSS3 动画 思维导图
    部署Seafile服务
    AngularJS 学习笔记
    Bootstrap3 学习笔记
    CSS 弹性盒
    传送门(portal)
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/AT2366.html
Copyright © 2011-2022 走看看