首页 > 其他 > 详细

bzoj2111【ZJOI2010】Perm 排列计数

时间:2016-02-12 22:12:36      阅读:424      评论:0      收藏:0      [点我收藏+]

2111: [ZJOI2010]Perm 排列计数

Time Limit: 10 Sec  Memory Limit: 259 MB
Submit: 1548  Solved: 321
[Submit][Status][Discuss]

Description

称一个1,2,...,N的排列P1,P2...,Pn是Magic的,当且仅当2<=i<=N时,Pi>Pi/2. 计算1,2,...N的排列中有多少是Magic的,答案可能很大,只能输出模P以后的值

Input

输入文件的第一行包含两个整数 n和p,含义如上所述。

Output

输出文件中仅包含一个整数,表示计算1,2,?, ???的排列中, Magic排列的个数模 p的值。

Sample Input

20 23

Sample Output

16

HINT

100%的数据中,1 ≤ ??? N ≤ 106, P??? ≤ 10^9,p是一个质数。 数据有所加强

Source




可以发现这n个点构成了一个树形结构,其中1是根节点,i的左二子是i<<1,右儿子是i<<1|1。

我们就可以用动态规划

设f[i]表示以i为根的子树的方案数,s[i]为子树大小。

则f[i]=f[i<<1]*f[i<<1|1]*c(s[i]-1,s[i<<1]),最终答案为f[1]。

现在问题转化为如何求c(s[i]-1,s[i<<1])。注意到p是质数,所以预处理阶乘和阶乘的逆元+Lucas定理解决。





#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<cstring>
#include<algorithm>
#define F(i,j,n) for(int i=j;i<=n;i++)
#define D(i,j,n) for(int i=j;i>=n;i--)
#define ll long long
#define maxn 1000005
using namespace std;
inline int read()
{
	int x=0,f=1;char ch=getchar();
	while (ch<'0'||ch>'9'){if (ch=='-') f=-1;ch=getchar();}
	while (ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
	return x*f;
}
ll n,m,p;
ll s[maxn],f[maxn],fac[maxn],inv[maxn];
inline ll c(int n,int m)
{
	if (n<m) return 0;
	if (n<p&&m<p) return fac[n]*inv[m]%p*inv[n-m]%p;
	return c(n/p,m/p)*c(n%p,m%p)%p;
}
int main()
{
	n=read();p=read();
	fac[0]=1;
	F(i,1,n) fac[i]=fac[i-1]*i%p;
	inv[0]=inv[1]=1;
	F(i,2,n) inv[i]=(p/i+1)*inv[i-p%i]%p;
	F(i,2,n) inv[i]=inv[i]*inv[i-1]%p;
	D(i,n,1)
	{
		s[i]=s[i<<1]+s[i<<1|1]+1;
		f[i]=((i<<1)>n?1:f[i<<1])*((i<<1|1)>n?1:f[i<<1|1])%p*c(s[i]-1,s[i<<1])%p;
	}
	printf("%lld\n",f[1]);
}


bzoj2111【ZJOI2010】Perm 排列计数

原文:http://blog.csdn.net/aarongzk/article/details/50655471

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!