码迷,mamicode.com
首页 > 其他好文 > 详细

bzoj2111ZJ2010排列计数_solution

时间:2017-12-17 12:17:05      阅读:126      评论:0      收藏:0      [点我收藏+]

标签:自己   www   ble   post   完全二叉树   es2017   style   scan   problem   

技术分享图片

          -by bzoj 

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



考虑第i个位置上的数字的可能性只取决于第i/2位置上的数,以及剩余数集的大小,可以看出一个树形模型

考虑第i个位置上的数字只会影响第i*2与i*2+1两个位置的可能性,发现这是个二叉树(完全二叉树)

而且是类似小根堆的形式,于是这个树的形态固定,第1个位置上只能放1;

这启发我们进一步思考

对根(第1个位置)来说,他自己是数集中最小的那个,从剩下n-1个数字中,挑一些填满左子树的节点,剩下填右子树,由于只有数字的个数影响结果,所以对左右子树的填法可以看做一个类似的子问题递归进行;

于是有f[i]=C(sum[i<<1],sum[i<<1]+sum[i<<1|1])*f[i<<1]*f[i<<1|1];

C为组合数;

注意组合数的处理细节

O(nlogn);

代码:

#include<cstdio>
#define LL long long 
using namespace std;
int N;
LL P;
int sum[2000010];
LL inv[2000010];
LL Sqr(LL ,int );
void dfs(int );
LL dp(int );
LL C(int ,int );
int main()
{
    int i,j,k;
    scanf("%lld%lld",&N,&P);
    for(i=1;i<=N;i++)
        inv[i]=Sqr(i%P,P-2);
    dfs(1);
    printf("%lld",dp(1));
    return 0;
}
LL Sqr(LL x,int n){
    LL ret=1;
    while(n){
        if(n&1)
            (ret*=x)%=P;
        (x*=x)%=P,n>>=1;
    }
    return ret;
}
void dfs(int x){
    if((x<<1)<=N)
        dfs(x<<1);
    if((x<<1|1)<=N)
        dfs(x<<1|1);
    sum[x]=sum[x<<1]+sum[x<<1|1]+1;
}
LL dp(int x){
    LL a=1,b=1,ret=1;
    if((x<<1)<=N)
        a=dp(x<<1);
    if((x<<1|1)<=N)
        b=dp(x<<1|1);
    ret=a*b%P;(ret*=C(sum[x<<1],sum[x<<1]+sum[x<<1|1]))%=P;
    return ret;
}
LL C(int m,int n){
    int i,j=0;
    LL ret=1;
    for(i=1;i<=m;i++)
        if((n-i+1)%P!=0&&i%P!=0)
            (ret*=(((n-i+1ll)*inv[i])%P))%=P;
        else
            j+=((n-i+1)%P==0?1:0)+(i%P==0?-1:0);
    return  j>0?0ll:ret;
}

 

bzoj2111ZJ2010排列计数_solution

标签:自己   www   ble   post   完全二叉树   es2017   style   scan   problem   

原文地址:http://www.cnblogs.com/nietzsche-oier/p/8051461.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!