Zjoi2010排列计数Perm
生活随笔
收集整理的這篇文章主要介紹了
Zjoi2010排列计数Perm
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
這東西還是挺有思想的,道聽途說一些東西,問問DuanYue同志,然后自己打表畫樹推了推,就搞出來了。
首先根據p i>p i/2(向下取整)這種形式,如果線段樹學的好的人,一定能看出來,這是在唯一標號法標號后的形式,即父親的權值大于兩個兒子的權值,這是一個小根堆的樣子。
那問題就是求給定數目的數字,求其能構成小根堆的個數。
這是一個類似樹形dp的問題,我們設f[i]表示以當前節點為根所能構成小根堆的個數,那么有狀態轉移方程:
就是說當前節點及其子樹一共分配了size[i]個數字,然后分給了左右子樹,有上式Combine種可能,然后把左右兒子的貢獻轉移上來。
組合數還是老規矩打階乘及其逆元的表,用lucas定理求一下,因為不用的話模數小的時候會崩,模數大了lucas也可以自動求組合取模。
實現過程中沒有定義dp數組(其實size數組也沒必要),直接帶返回值的dfs搞一下。
代碼略丑,勿看(主要是懶,直接define int long long 了)。
#include<iostream> #include<algorithm> #include<cmath> #include<cstring> #include<cstdio> #include<vector> #include<queue> #define int long long using namespace std; int n,p; int size[2000000]; int fac[2000000],inv[2000000]; int qpow(int x,int k){int ans=1;for(;k;k>>=1,x=1ll*x*x%p)if(k&1) ans=1ll*ans*x%p;return ans; } void pre(){fac[0]=1;for(int i=1;i<min(n,p);i++)fac[i]=1ll*fac[i-1]*i%p;inv[min(n,p)-1]=qpow(fac[min(n,p)-1],p-2);for(int i=min(n,p)-1;i>=1;i--)inv[i-1]=1ll*inv[i]*i%p; } int com(int x,int y){if(y>x) return 0;return ((1ll*fac[x]*inv[y])%p*inv[x-y]%p)%p; } int lucas(int x,int y){if(!y) return 1;return com(x%p,y%p)*lucas(x/p,y/p)%p; } int dfs(int x){if(x>n) return 1;size[x]++;int a=dfs(x<<1)%p;int b=dfs(x<<1|1)%p;size[x]+=size[x<<1]+size[x<<1|1];return 1ll*((a*b)%p*(lucas(size[x]-1,size[x<<1])%p))%p; } signed main(){scanf("%lld%lld",&n,&p);pre();/*for(int i=1;i<=p;i++)cout<<fac[i]<<" ";cout<<endl;for(int i=1;i<=p;i++)cout<<inv[i]<<" ";cout<<endl;*/printf("%lld",dfs(1)%p);return 0; } View Code?
轉載于:https://www.cnblogs.com/Yu-shi/p/11121778.html
總結
以上是生活随笔為你收集整理的Zjoi2010排列计数Perm的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: DPM(物体检测)
- 下一篇: 4 VMware安装