1. 程式人生 > >洛谷P2606 [ZJOI2010]排列計數

洛谷P2606 [ZJOI2010]排列計數

不知道 計算 color pan pac sin gis n) flag

題目描述

稱一個1,2,...,N的排列P1,P2...,Pn是Magic的,當且僅當2<=i<=N時,Pi>Pi/2. 計算1,2,...N的排列中有多少是Magic的,答案可能很大,只能輸出模P以後的值

輸入輸出格式

輸入格式:

輸入文件的第一行包含兩個整數 n和p,含義如上所述。

輸出格式:

輸出文件中僅包含一個整數,表示計算1,2,?, ???的排列中, Magic排列的個數模 p的值。

輸入輸出樣例

輸入樣例#1:
20 23 
輸出樣例#1:
16

說明

100%的數據中,1 ≤N ≤ 10^6, P≤ 10^9,p是一個質數。

題目大意:求1--n能構成小根堆的排列

題解:

暴力30...

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
int n,p,ans,a[1000009];
int main(){
    scanf("%d%d",&n,&p);
    for(int i=1;i<=n;i++)a[i]=i;
    do{
        bool flag=false;
        for(register int i=2;i<=n;i++){
            if(a[i]<a[i/2
]){ flag=true;break; } } if(!flag)ans=(ans%p+1%p)%p; }while(next_permutation(a+1,a+n+1)); printf("%d\n",ans); return 0; }

正解:Lucas定理+樹形dp

沒看出來是小根堆...我這個沙茶...

然後根一定是最小的,然後f[i]=c(s[i]-1,s[i<<1])*f[l]*f[r]

f[i]表示以i為根的小根堆的數量....然後左子樹的大小就是從s[i]-1(減去根

中選出s[i<<1],用Lucas定理求就行啦...

因為有子問題的....

ps:不知道為什麽一直WA,抱著試試看的心態,我多加了一個取模。

你猜怎麽著?就A了....

#include<iostream>
#include<cstdio>
#include<cstring>
#define maxn 1000009
#define LL long long
using namespace std;

LL n,p;
LL f[maxn],inv[maxn],s[2*maxn],dp[maxn];

LL ksm(LL x,LL y){
    LL ret=1%y;
    while(y){
        if(y&1)ret=(1LL*ret*x)%p;
        x=1LL*x*x%p;
        y>>=1;
    }
    return ret;
}

void pre(){
    f[0]=inv[0]=1;
    for(int i=1;i<=n;i++)f[i]=(1LL*f[i-1]*i)%p;
    for(int i=1;i<=n;i++)inv[i]=ksm(f[i],p-2)%p;
}

LL C(LL n,LL m){
    return 1LL*f[n]*inv[m]%p*inv[n-m]%p;
}
 
LL Lucas(LL n,LL m){
    if(!m)return 1;
    return C(n%p,m%p)*Lucas(n/p,m/p)%p;
}

int main(){
    scanf("%lld%lld",&n,&p);
    pre();
    for(int i=n;i>=1;i--){
        s[i]=s[i<<1]+s[i<<1|1]+1;
        dp[i]=Lucas(s[i]-1,s[i<<1])%p;
        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;
        dp[i]=dp[i]%p;
    }
    printf("%lld\n",dp[1]%p);
    return 0;
}

洛谷P2606 [ZJOI2010]排列計數