Perm排列计数
B. Perm 排列计数
内存限制:512 MiB 时间限制:1000 ms 标准输入输出
题目描述
输入格式
输出格式
样例
数据范围与提示
题解
刚拿到这道题的时候没什么思路,但脑子啊有时候吧~~
可以把这个题想象成一棵二叉树,下标即在排列中的位置,当然1为根
一个点的任意一个子孙一直除2的话最终都会到该点,即在以该点为根的子树内,该点值最小
假设有n个点,父亲要最小的那一个,左右儿子各自成家,互不干扰,左儿子要剩下的n-1个中的m个,剩下的都给了右儿子一家,组合数为C(n-1,m),向下一个个分下去你会发现
转移式为 f[爹]=f[左儿子]*f[右儿子]*C(size[],size[]) f[]表示满足条件的组合数,size[]表示以该点为根的树的大小
因为n有点大,n!会炸掉,所以求组合数的时候上Lucas定理就欧了
弱弱的Lockey死活不用Lucas(我牛逼,我伟大),一直在搞高精乘低精,高精除低精,但还是在强悍的Lucas面前献上了膝盖%%%%
#include<iostream> #include<cstdio> using namespace std; int n,p,son[110000000],d[100000000]; long long ans=1; long long pow(long long a,long long b,long long p){ long long ans=1; a%=p; while(b){ if(b&1) ans=(ans*a)%p; b>>=1; a=(a%p)*(a%p)%p; } ans%=p; return ans; } long long inv(long long x,long long p){ return pow(x,p-2,p); } long long C(long long n,long long m){ if(m>n) return 0; long long up=1,down=1; for(int i=n-m+1;i<=n;i++) up=up*i%p; for(int i=1;i<=m;i++) down=down*i%p; return up*inv(down,p)%p; } long long Lucas(long long n,long long m,long long p){ if(m==0) return 1; return C(n%p,m%p)*Lucas(n/p,m/p,p); } void dfs(int x){ if(2*x<=n) dfs(2*x); if(2*x+1<=n) dfs(2*x+1); son[x]=son[2*x]+son[2*x+1]+1; if(son[x]>2){ ans=(long long)(ans%p)*(long long)(Lucas(son[x]-1,son[2*x]?son[2*x]:son[2*x+1],p)%p)%p; } return; } int main(){ scanf("%d%d",&n,&p); if(n==1){ cout<<1%p; return 0; } dfs(1); cout<<ans%p; }
$Will$ $Be$ $The$ $King$