对于给定的一个长度为n的序列{B[n]},问有多少个序列{A[n]}对于所有的i满足:A[1]~A[i]这i个数字中有恰好B[i]个数字小等于i。其中{A[n]}为1~n的一个排列,即1~n这n个数字在序列A[I]中恰好出现一次。
数据保证了至少有一个排列满足B序列。
数据保证了至少有一个排列满足B序列。
Input
输入的第1行为一个正整数N,表示了序列的长度。
第2行包含N个非负整数,描述了序列{B[i]}。
第2行包含N个非负整数,描述了序列{B[i]}。
Output
输出仅包括一个非负整数,即满足的{A[i]}序列个数。
Sample Input
3
0 1 3
0 1 3
Sample Output
3
【样例说明】
对于A序列为1~3的全排列分别对应的B序列如下(冒号左边为A序列,冒号右边为对应B的序列)
1 2 3:1 2 3
1 3 2:1 1 3
2 1 3:0 2 3
2 3 1:0 1 3
3 1 2:0 1 3
3 2 1:0 1 3
所以有3个满足的A序列。
【数据说明】
对于20%的数据,有N≤8;
对于30%的数据,有N≤11且答案不大于20000;
对于50%的数据,有N≤100;
对于100%的数据,有N≤2000。
对于A序列为1~3的全排列分别对应的B序列如下(冒号左边为A序列,冒号右边为对应B的序列)
1 2 3:1 2 3
1 3 2:1 1 3
2 1 3:0 2 3
2 3 1:0 1 3
3 1 2:0 1 3
3 2 1:0 1 3
所以有3个满足的A序列。
【数据说明】
对于20%的数据,有N≤8;
对于30%的数据,有N≤11且答案不大于20000;
对于50%的数据,有N≤100;
对于100%的数据,有N≤2000。
Solution :考试的时候没有什么想法,就按照组合数常见的思路想,但是最后还是暴力了。在网上看见有十分巧妙的建模,设想有一个01矩阵,对于一 个元素a[i][j],i表示这是第i个数,而这个数是什么,就看j,倘若a[i][j]的值是1,则表示第i个位置的数为j。这样建模的话对于每一个b[i],就相 当于以[i][i]为右下角的矩阵中,有b[i]个1,否则就是不合法的排列。同时相邻的b[i],不可能差值超过2,那么分类讨论之后就会有一个累加的 式子,当差为1时ans *= 2*i-1 - 2*b[i-1],差为2时ans *= sqr(i-1 - b[i-1])
Ps.高精度(答案不会超过n!)。
#include<cstdio>
#define Ya 10
#define maxn 6000
using namespace std;
int b[maxn],n,push,x,ans[maxn];
void init(){
scanf("%d",&n);
for (int i = 1;i <= n;i ++) scanf("%d",&b[i]);
}
void mul(int x){
push = 0;
for (int i = 1;i <= ans[0];i ++){
ans[i] = ans[i] * x + push;
push = ans[i] / Ya;
ans[i] %= Ya;
}
while (push){
ans[++ans[0]] = push;
push = ans[ans[0]] / Ya;
ans[ans[0]] %= Ya;
}
}
void work(){
ans[0] = 1;
ans[1] = 1;
for (int i = 1; i <= n;i ++){
if (b[i] - b[i-1] == 1){
x = 2*i-1 - 2*b[i-1];
mul(x);
}
if (b[i] - b[i-1] == 2){
x = i-1 - b[i-1];
x *= x;
mul(x);
}
}
}
void print(){
for (int i = ans[0];i >= 1;i --) printf("%d",ans[i]);
}
int main(){
init();
work();
print();
}