Zjoi2010排列计数Perm
這東西還是挺有思想的,道聽(tīng)途說(shuō)一些東西,問(wèn)問(wèn)DuanYue同志,然后自己打表畫(huà)樹(shù)推了推,就搞出來(lái)了。
首先根據(jù)p i>p i/2(向下取整)這種形式,如果線段樹(shù)學(xué)的好的人,一定能看出來(lái),這是在唯一標(biāo)號(hào)法標(biāo)號(hào)后的形式,即父親的權(quán)值大于兩個(gè)兒子的權(quán)值,這是一個(gè)小根堆的樣子。
那問(wèn)題就是求給定數(shù)目的數(shù)字,求其能構(gòu)成小根堆的個(gè)數(shù)。
這是一個(gè)類(lèi)似樹(shù)形dp的問(wèn)題,我們?cè)O(shè)f[i]表示以當(dāng)前節(jié)點(diǎn)為根所能構(gòu)成小根堆的個(gè)數(shù),那么有狀態(tài)轉(zhuǎn)移方程:
就是說(shuō)當(dāng)前節(jié)點(diǎn)及其子樹(shù)一共分配了size[i]個(gè)數(shù)字,然后分給了左右子樹(shù),有上式Combine種可能,然后把左右兒子的貢獻(xiàn)轉(zhuǎn)移上來(lái)。
組合數(shù)還是老規(guī)矩打階乘及其逆元的表,用lucas定理求一下,因?yàn)椴挥玫脑捘?shù)小的時(shí)候會(huì)崩,模數(shù)大了lucas也可以自動(dòng)求組合取模。
實(shí)現(xiàn)過(guò)程中沒(méi)有定義dp數(shù)組(其實(shí)size數(shù)組也沒(méi)必要),直接帶返回值的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?
轉(zhuǎn)載于:https://www.cnblogs.com/Yu-shi/p/11121778.html
總結(jié)
以上是生活随笔為你收集整理的Zjoi2010排列计数Perm的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: DPM(物体检测)
- 下一篇: 4 VMware安装