ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

[组合数学]:Perm 排列计数

2019-07-04 12:03:37  阅读:277  来源: 互联网

标签:排列 return siz long 计数 Perm fac include size


题干:

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,⋯, n的排列中, Magic排列的个数模 p的值。
Sample Input
20 23
Sample Output
16
HINT
100%的数据中,1 ≤ N ≤ 106, P≤ 10^9,p是一个质数。

题目概述:求N个数的排列中满足P[i]>P[i/2]的个数。

 


 

题解:

拿到这道题一脸懵比,自己想了半天妄图用纯组合数学知识做出来。

问了问大佬,大佬说要用小根堆,吓得我直接就不是人了。

研究了一下,发现这道题其实就是求n个数组成的小根堆的个数。

写出来一个状态转移方程(搞得跟树归似的吓死个人):f[i]=f[i<<1]+f[i<<1|1]+C(size[i-1],size[i<<1]);

解释一下:上式中,size代表小根堆(其实就是一个树型的)以某一点为根节点的子树的大小。

f代表以当前节点为根节点的小根堆共有多少中排列方式。

C(size[i-1],size[i<<1])代表的意义是:从比i大的数字中选出左儿子需要的个数插入到左子树中组成的一种排列。

其实C(size[i-1],size[i<<1])和C(size[i-1],size[i<<1|1])还是一样的。

size的累加过程:siz[i]=siz[i<<1]+siz[i<<1|1]+1;

我们发现,n的范围还是不小的(10的6次方),所以用到了Lucas定理。就这样啦~

代码:

 

#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#include<cmath>
#include<cstdlib>
using namespace std;
long long n,p,siz[4000006],dp[4000006];
long long fac[40000006];
inline long long qpow(long long a,long long b)
{
    register long long ans=1;
    a%=p;
    while(b)
    {
        if(b&1)
            ans=ans*a%p;
        a=a*a%p;
        b>>=1;
    }
    return ans;
}
inline long long C(long long nn,long long k)
{
    if(k>nn)return 0;
    else
        return fac[nn]*(qpow(fac[k]*fac[nn-k]%p,p-2))%p;
}
inline long long Lucas(long long a,long long b)
{
    if(b==0)
        return 1;
    return C(a%p,b%p)*Lucas(a/p,b/p)%p;
}
inline void getchart()
{
    fac[1]=fac[0]=1;
    for(register long long i=2;i<=n;i++)
        fac[i]=(fac[i-1]*i)%p;
    return ;
}
int main()
{
    scanf("%lld %lld",&n,&p);
    getchart();
//    cout<<fac[10]<<endl;
    for(register int i=n;i>=1;--i)
    {
            siz[i]=siz[i<<1]+siz[i<<1|1]+1;
            dp[i]=Lucas(siz[i]-1,siz[i<<1]);
            if((i<<1)<=n)
            dp[i]=(dp[i]*dp[i<<1])%p;
            if((i<<1|1)<=n)
            dp[i]=(dp[i]*dp[i<<1|1])%p;
    }
    printf("%lld\n",dp[1]);
    return 0;
}
代码在这里

 

标签:排列,return,siz,long,计数,Perm,fac,include,size
来源: https://www.cnblogs.com/xingmi-weiyouni/p/11131530.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有