[ZJOI2010]Perm 排列计数
题概:
题目描述
输入格式
输出格式
样例
数据范围与提示
分析:
主要意思就是在
1-n的序列里找到P(i)>P(i/2)
可以想到二叉堆满足
父亲比儿子优的性质
所以即维护小根堆从root到叶子的路满足一个magic序列
(1)首先是DP
因为儿子之间是可以交换的
所以有不同的二叉树
DP即是求二叉树的种数
dp[i]=dp[i*2]*dp[i*2+1]*C(size[i]-1,size[left]);
始终没有理解式子
其实
这样一个小根堆的size,形状都是相对确定的
根据乘法原理
有两个儿子相乘
另外,考虑两个儿子树中的任意节点可交换
因此有*C(size[i]-1,size[left])
始终没有理解式子
其实
这样一个小根堆的size,形状都是相对确定的
根据乘法原理
有两个儿子相乘
另外,考虑两个儿子树中的任意节点可交换
因此有*C(size[i]-1,size[left])
(2)可以看到
1 ≤ ??? N ≤ 106, P??? ≤ 10^9
这样的数据范围
需要有lucas
博主因实力太菜
实际上花了半天在调lucas
最后
#include<iostream>
#include<cstdio>
#define ll long long
#define MAXN 2000010
using namespace std;
int n;
ll p;
ll jc[MAXN];
ll dp[MAXN];
int size[MAXN];
ll pow(ll a,ll b){
ll ans=1;
while(b){
if(b&1)ans=(ans*a)%p;
a=(a*a)%p;
b>>=1;
}
return ans%p;
}
ll C(int a,int b){
if(a<b)return 0;
if(b==0)return 1;
return jc[a]*pow(jc[a-b]*jc[b]%p,p-2)%p;
}
ll lucas(int a,int b){
if(b>a)return 0;
if(b==0)return 1;
if(a>p||b>p)return C(a%p,b%p)*lucas(a/p,b/p)%p;
return C(a,b)%p;
}
void dfs(ll u){
if(u>n){
dp[u]=1;
return ;
}
dfs(u<<1);
dfs(u<<1|1);
size[u]=size[u<<1]+size[u<<1|1]+1;
dp[u]=dp[u<<1]*dp[u<<1|1]%p*lucas(size[u]-1,size[u<<1])%p;
return ;
}
int main(){
scanf("%d%lld",&n,&p);
jc[0]=1;
for(int i=1;i<=n;++i)jc[i]=jc[i-1]*1ll*i%p;
dfs(1);
printf("%lld\n",dp[1]%p);
}