Perm 排列计数
题目描述
输入格式
输出格式
样例
数据范围与提示
刚刚学了组合数学,然后就水了道题。
模板一定要好好学,一个取模颓老半天。。。。。
然后开始说正经的
首先这题仔细想想可以看成二叉堆(满足大小关系嘛。。。)
然后就可以像个树规似的
从最下的节点看是向上转移
设f数组表示方案数size数组表示子树大小
因为每个节点的左子树的方案树与右子树方案数相乘并没有枚举出全部结果
仔细想想每个节点的左右子树的数是可以互换的这样依旧满足二叉堆性质
即 C(size[i]-1,size[left])可表示
然后数据范围可以用卢卡斯定理取模
这就不解释了反正网上很多。。。。
C(n,m)%p=C(n%p,m%p)*C(n/p,m/p)%p
总之最后转移的式子
f[x]=f[x*2]*f[x*2+1]*C(size[x]-1,size[x*2])
蒟蒻第一次写博客如有不当请指正
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<cstdio> 2 #include<iostream> 3 #include<cmath> 4 #include<algorithm> 5 #include<cstring> 6 #define MAXN 2006001 7 #define ll long long 8 using namespace std; 9 ll sum[MAXN]; 10 ll size[MAXN],f[MAXN]; 11 ll n,p; 12 ll pow(ll x,ll y) 13 { 14 ll ans=1; 15 while(y>0) 16 { 17 if(y&1)ans=ans*x%p; 18 x=x*x%p; 19 y>>=1; 20 } 21 return ans%p; 22 } 23 ll getsum(ll x,ll y) 24 { 25 if(y>x) return 0; 26 if(!y)return 1; 27 return (sum[x]*pow(sum[y]*sum[x-y]%p,p-2)%p)%p; 28 } 29 ll lucas(ll x,ll y) 30 { 31 if(y>x)return 0; 32 if(y==0)return 1; 33 if(x>p||y>p)return lucas(x/p,y/p)*getsum(x%p,y%p)%p; 34 return getsum(x,y)%p; 35 } 36 void find(ll k) 37 { 38 if(k>n){f[k]=1;return ;} 39 find(k*2);find(k*2+1); 40 size[k]=size[k*2]+size[k*2+1]+1; 41 f[k]=f[k*2]*f[k*2+1]%p*lucas(size[k]-1,size[k*2])%p; 42 } 43 int main() 44 { 45 scanf("%lld%lld",&n,&p); 46 sum[0]=1;sum[1]=1; 47 for(ll i=2;i<=n;++i)sum[i]=((sum[i-1]*1ll*i)%p); 48 find(1); 49 printf("%lld\n",f[1]%p); 50 }