zoukankan      html  css  js  c++  java
  • bzoj 2111 [ZJOI2010]Perm 排列计数(DP+lucas定理)

    【题目链接】

        http://www.lydsy.com/JudgeOnline/problem.php?id=2111

    【题意】

       

        给定n,问1..n的排列中有多少个可以构成小根堆。

    【思路】

        设f[i]为i个数的方案,设l为左子树大小r为右子树大小,则有:

            f[i]=C(i-1,l)*f[l]*f[r]

        因为是个堆,所以子树大小都是确定的,可以直接递推得到。

        其中C(n,m) nm比较大,可以用lucas定理求。

        模型建立的重要性可知一二。。。   

    【代码】

     1 #include<cstdio>
     2 #include<iostream>
     3 using namespace std;
     4 
     5 typedef long long ll;
     6 const int N = 5e6+10;
     7 
     8 int mod,n;
     9 ll f[N],fac[N],s[N];
    10 
    11 ll pow(ll a,ll p,int mod)
    12 {
    13     ll ans=1;
    14     while(p) {
    15         if(p&1) ans=(ans*a)%mod;
    16         a=(a*a)%mod; p>>=1;
    17     }
    18     return ans;
    19 }
    20 
    21 void get_pre(int n)
    22 {
    23     fac[0]=1;
    24     for(int i=1;i<=n;i++)
    25         fac[i]=(fac[i-1]*i)%mod;
    26 }
    27 ll C(ll n,ll m,int mod)
    28 {
    29     if(n<m) return 0;
    30     if(n<mod&&m<mod) {
    31         ll invn=pow(fac[n-m],mod-2,mod);
    32         ll invm=pow(fac[m],mod-2,mod);
    33         return fac[n]*invm%mod*invn%mod;
    34     }
    35     return C(n/mod,m/mod,mod)*C(n%mod,m%mod,mod)%mod;
    36 }
    37 
    38 int main()
    39 {
    40     scanf("%d%d",&n,&mod);
    41     get_pre(min(n,mod));
    42     for(int i=n;i;i--) {
    43         s[i]=s[i<<1]+s[i<<1|1]+1;
    44         f[i]=C(s[i]-1,s[i<<1],mod);
    45         if((i<<1)<=n) f[i]=(f[i]*f[i<<1])%mod;
    46         if((i<<1|1)<=n) f[i]=(f[i]*f[i<<1|1])%mod;
    47     }
    48     printf("%lld
    ",f[1]);
    49     return 0;
    50 }
  • 相关阅读:
    python-socket1
    python-网络编程
    linux-常用指令3
    linux-vim/编辑器
    linux-常用指令2
    linux-常用指令1
    MySQL 练习题
    33 python 并发编程之IO模型
    32 python 并发编程之协程
    31 python下实现并发编程
  • 原文地址:https://www.cnblogs.com/lidaxin/p/5350250.html
Copyright © 2011-2022 走看看