[JZOJ 1283]排序统计

Description
  对于给定的一个长度为n的序列{B[n]},问有多少个序列{A[n]}对于所有的i满足:A[1]~A[i]这i个数字中有恰好B[i]个数字小等于i。其中{A[n]}为1~n的一个排列,即1~n这n个数字在序列A[I]中恰好出现一次。 
  数据保证了至少有一个排列满足B序列。
Input
  输入的第1行为一个正整数N,表示了序列的长度。 
  第2行包含N个非负整数,描述了序列{B[i]}。

Output
  输出仅包括一个非负整数,即满足的{A[i]}序列个数。

Sample Input
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。
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();
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值