「ZJOI 2010」 排列計數
阿新 • • 發佈:2019-03-26
.org http return 方案 現在 ++ 計數 cout while
題目鏈接
戳我
\(Solution\)
其實我們可以發現這題等價於讓你求:
用\(1\)~\(n\)的數組成一個完全二叉樹使之滿足小根堆性質的方案數
於是我們可以考慮\(dp\)
假設我們現在在\(i\)點,\(i\)的子節點個數為\(s[i]\)(包括自己)
則:
\(dp[i]=C(s[i]-1,s[i*2])*f[i*2]*f[i*2+1]\)
\(ps:\)
因為是二叉樹所以\(i*2\)和\(i*2+1\)為\(i\)的兩個兒子
這個式子很容易看懂吧。
在子節點中選一些填入左兒子,一些填入右兒子,右兒子和左兒子都要滿足小根堆的性質
\(Code\)
#include<bits/stdc++.h> #define rg register #define int long long using namespace std; int read() { int x=0,f=1;char c=getchar(); while(c<'0'||c>'9') f=(c=='-')?-1:1,c=getchar(); while(c>='0'&&c<='9') x=x*10+c-48,c=getchar(); return f*x; } int f[1000001],s[2000011],dp[2000011]; int ksm(int a,int b,int p){ int ans=1; while(b){ if(b&1) ans=ans*a%p; a=a*a%p,b>>=1; } return ans; } int c(int n,int m,int p){return f[n]*ksm(f[m]*f[n-m]%p,p-2,p)%p;} int lucas(int n,int m,int p){ return m?c(n%p,m%p,p)*lucas(n/p,m/p,p)%p:1; } main(){ int p,n; cin>>n>>p,f[0]=1; for(int i=1;i<=n;i++) f[i]=f[i-1]*i%p; for(int i=1;i<=n;i++) s[i]=1; for(int i=n;i>=2;i--) s[i>>1]+=s[i]; for(int i=n+1;i<=n*2+1;i++) dp[i]=1; for(int i=n;i>=1;i--) dp[i]=lucas(s[i]-1,s[i*2],p)%p*dp[i*2]%p*dp[i*2+1]%p; cout<<dp[1]; }
「ZJOI 2010」 排列計數