[FJOI2016] 建築師

題目描述

小 Z 是一個很有名的建築師,有一天他接到了一個很奇怪的任務:在數軸上建 n 個建築,每個建築的高度是 1n 之間的一個整數。小 Z 有很嚴重的強迫症,他不喜歡有兩個建築的高度相同。另外小 Z 覺得如果從最左邊(所有建築都在右邊)看能看到 A 個建築,從最右邊(所有建築都在左邊)看能看到 B 個建築,這樣的建築羣有着獨特的美感。現在,小 Z 想知道滿足上述所有條件的建築方案有多少種?

如果建築 i 的左(右)邊沒有任何建造比它高,則建築 i 可以從左(右)邊看到。兩種方案不同,當且僅當存在某個建築在兩種方案下的高度不同。

輸入格式

第一行一個整數 T ,代表 T 組數據。

接下來 T 行,每行三個整數 nAB

輸出格式

對於每組數據輸出一行答案 mod 109+7

樣例輸入

2
3 2 2
3 1 2

樣例輸出

2
1

數據規模與約定

對於 10% 的數據,1n10

對於 20% 的數據,1n100

對於 40% 的數據,1n500001T5

對於 70% 的數據,1n500001T2000

對於 100% 的數據,1n500001AB1001T200000

題解

首先,我們考慮暴力該怎麼做。
fi,j,k 爲 前 i 箇中, 從左邊能看到 j 個, 從右邊能看到 k 個的方案數。
fi,j,k=fi1,j1,k+fi1,j,k1+fi1,j,k×(i2)
因爲左右兩邊是鏡像關係,所以我們只要處理出 fi,j 爲放了 i 個,從一側能看到 j 個的方案數即可。
這樣就有 40 分了。
然後我們設去掉最高的點之後, 當前從左邊能看到的爲 a 個, 從右邊能看到的爲 b 個,我們每次加一條邊有三種情況。
第一種情況爲 a+1 , 第二種情況爲 b+1 , 第三種情況爲 a,b 不變。
對於第一第二種都各有 1 種情況。
對於第三種的可能隨着我們選邊的進行從 n2 遞減到 0
則設 fi,j 爲選了 i 個點,有 j 個點改變了 ab 的方案數。
fi,j=fi1,j1+fi1,j×(i1)
然後其中有 A1 個點是用來改變 a 的。
則最後的答案爲 CA+B2A1×fn1,A+B2
然後這是個斯特林數???
嗯沒錯,就是個第一類斯特林數。
所以答案爲 CA+B2A1×Sn1A+B2
額說一下爲什麼是第一類斯特林數吧。
我們設除去最高的點之後,每個點之後被擋住了 x 個點。
則這 x 個點有 x! 種方案。
算上擋住這些點的固定的那一個點,則這些點的方案數即爲這些點的圓排列的方案數。
所以題意即爲在去掉最高點之後的 n1 個數中劃分出 A+B2 個圓排列並選出 A1 個放在左邊。
所以答案如上。
不知道有人是怎麼打表看出來的,真的是神仙啊。

代碼

#include <algorithm>
#include <cstdio>
#include <cmath>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <iostream>
#include <queue>
#include <set>
#include <stack>

#define R register
#define ll long long
#define db double
#define sqr(_x) (_x) * (_x)
#define Cmax(_a, _b) ((_a) < (_b) ? (_a) = (_b), 1 : 0)
#define Cmin(_a, _b) ((_a) > (_b) ? (_a) = (_b), 1 : 0)
#define Max(_a, _b) ((_a) > (_b) ? (_a) : (_b))
#define Min(_a, _b) ((_a) < (_b) ? (_a) : (_b))
#define Abs(_x) (_x < 0 ? (-(_x)) : (_x))

using namespace std;

namespace Dntcry
{
    inline int read()
    {
        R int a = 0, b = 1; R char c = getchar();
        for(; c < '0' || c > '9'; c = getchar()) (c == '-') ? b = -1 : 0;
        for(; c >= '0' && c <= '9'; c = getchar()) a = (a << 1) + (a << 3) + c - '0';
        return a * b;
    }
    inline ll lread()
    {
        R ll a = 0, b = 1; R char c = getchar();
        for(; c < '0' || c > '9'; c = getchar()) (c == '-') ? b = -1 : 0;
        for(; c >= '0' && c <= '9'; c = getchar()) a = (a << 1) + (a << 3) + c - '0';
        return a * b;
    }
    const int Maxn = 50010, Maxl = 210, Mod = 1000000007;
    int T, n, A, B;
    ll S[Maxn][Maxl], C[Maxl][Maxl];
    void init(R int tmp1, R int tmp2)
    {
        for(R int i = 0; i <= tmp1; i++)
        {
            S[i][0] = 0;
            if(i <= tmp2) S[i][i] = 1;
            for(R int j = 1; j <= Min(i, tmp2); j++) 
                S[i][j] = (S[i - 1][j - 1] + (i - 1) * S[i - 1][j] % Mod) % Mod;
        }
        for(R int i = 0; i <= tmp2; i++) 
        {
            C[i][0] = 1;
            for(R int j = 1; j <= i; j++) 
                C[i][j] = (C[i - 1][j] + C[i - 1][j - 1]) % Mod;
        }
        return ;
    }
    int Main()
    {
        init(50000, 200);
        T = read();
        while(T--)
        {
            n = read(), A = read(), B = read(); 
            printf("%lld\n", S[n - 1][A + B - 2] * C[A + B - 2][A - 1] % Mod);
        }
        return 0;
    }
}
int main()
{
    return Dntcry :: Main();
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章