• [题解] [FJOI2016] 建筑师


    题面

    题解

    题意所给即为前缀最大值有 (A) 种, 后缀最大值有 (B)

    由于 (n) 是肯定为最大值的, 也就是说, 其他 (A - 1) 个前缀最大值都在 (n) 左边出现, (B - 1) 个后缀最大值都在 (n) 右边出现

    那么每一种方案就相当于把 (n) 单独拿出来, 其他的分为 (A + B - 2)

    若这一组的最大值是前缀最大值, 则这一组最高的必须要放在第一个, 其他的放在他后面随意排列

    若这一组的最大值是后缀最大值, 则这一组最高的必须要放在最后一个, 其他的放在他前面随意排列

    不难发现这就是圆排列, 对于每一个圆排列从最高的那个地方断开展开后就变成了这一组排列后的样子

    并且这 (A + B - 2) 组的最大值互不相同, 也就是说只要从这其中选出 (A - 1) 组出来按每一组的最大值从小到大放在 (n) 的左边就有了 (A) 个前缀最大值, 后缀最大值同理可得

    那么总方案数就是

    [displaystyle egin{bmatrix}n-1\A+B-2end{bmatrix}*inom{A+B-2}{A-1} ]

    即将 (n - 1) 个数分为 (A + B - 2) 个圆排列, 然后任选 (A - 1) 组出来放在 (n) 左边, 由于大小顺序确定, 不需要再算上排列的方案数

    Code

    #include <algorithm>
    #include <iostream>
    #include <cstring>
    #include <cstdio>
    const int N = 205;
    const int mod = 1e9 + 7; 
    using namespace std;
    
    int c[N][N], T, n, A, B, s[50005][N]; 
    
    template < typename T >
    inline T read()
    {
    	T x = 0, w = 1; char c = getchar();
    	while(c < '0' || c > '9') { if(c == '-') w = -1; c = getchar(); }
    	while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
    	return x * w; 
    }
    
    int main()
    {
    	T = read <int> (); 
    	for(int i = 0; i <= 200; i++)
    		for(int j = 0; j <= i; j++)
    			c[i][j] = (!j ? 1 : (c[i - 1][j] + c[i - 1][j - 1]) % mod); 
    	s[0][0] = 1;
    	for(int i = 1; i <= 50000; i++)
    		for(int j = 1; j <= min(i, 200); j++)
    			s[i][j] = (s[i - 1][j - 1] + 1ll * s[i - 1][j] * (i - 1) % mod) % mod; 
    	while(T--)
    	{
    		n = read <int> (), A = read <int> (), B = read <int> (); 
    		printf("%lld
    ", 1ll * s[n - 1][A + B - 2] * c[A + B - 2][A - 1] % mod); 
    	}
    	return 0; 
    }
    
  • 相关阅读:
    读写文本文件 ---字符行式读取
    【编程之美挑战赛第一场】活动中心
    Jetty开发指导:框架
    Java实现BASE64编解码
    关于BT下载的一点事儿
    R语言学习笔记
    完毕port(CompletionPort)具体解释
    微软2014校园招聘笔试试题
    hdu-4857-逃生-拓扑排序
    概率论高速学习03:概率公理补充
  • 原文地址:https://www.cnblogs.com/ztlztl/p/12207533.html
Copyright © 2020-2023  润新知