0%

[FJOI2016] 建筑师

基本功太差导致的。

题目链接

题意

组询问,每一次询问所有长度为 的排列中,前缀最大值恰有 个,后缀最大值恰有 个的排列个数。

题解

考虑没有 的限制怎么做。

首先可以发现,每一个合法的排列都可以被分成恰好 段,满足每一段的第一个数是其中的最大值,且每一段的第一个数字递增。

可以设 表示 个数字,分成 段的方案数。

考虑最后一个数字,它既可以自己独立成段(要求为整体最大值),要么直接跟着原来的最后一段。

所以可以得到转移方程 ,边界情况为

答案就是

其实还可以发现, 就是第一类斯特林数 ,虽然并没有用。

再来想加上 的限制怎么做。

考虑整个数列的最大值 ,它一定是最后一个前缀最大值,也是最靠前的后缀最大值。

那么 就将整个序列分为了两部分,前面一部分是前缀最大值个数为 的排列,后面一部分是后缀最大值个数为 的排列。

所以我们可以得到答案为 ,显然不能通过。

其实我们可以延续上面的思想。

可以发现,整个序列除了 以外的所有元素,恰好被分为了 段,其中 左边 段,右边 段。

那么我们直接放一起计数就好了。

所以整体答案是

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
#include<bits/stdc++.h>
#define inf 0x3f3f3f3f3f3f3f3fll
#define debug(x) cerr<<#x<<"="<<x<<endl
using namespace std;
using ll=long long;
using ld=long double;
using pli=pair<ll,int>;
using pi=pair<int,int>;
template<typename A>
using vc=vector<A>;
inline int read()
{
int s=0,w=1;char ch;
while((ch=getchar())>'9'||ch<'0') if(ch=='-') w=-1;
while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar();
return s*w;
}
const int mod=1000000007;
int dp[50001][201];
int C[201][201];
inline void init()
{
int N=200,M=50000;
for(int i=0;i<=N;i++)
{
C[i][0]=C[i][i]=1;
for(int j=1;j<i;j++) C[i][j]=(C[i-1][j]+C[i-1][j-1])%mod;
}
dp[0][0]=1;
for(int i=1;i<=M;i++) for(int j=1;j<=N;j++)
dp[i][j]=(dp[i-1][j-1]+(ll)dp[i-1][j]*(i-1))%mod;
}
int main()
{
init();
int T=read();
while(T--)
{
int n=read(),A=read(),B=read();
printf("%lld\n",(ll)dp[n-1][A+B-2]*C[A+B-2][A-1]%mod);
}
return 0;
}

感谢观看!