P2606 ZJOI2010 排列計數
阿新 • • 發佈:2020-07-23
\[gcd(a,p)=1~~a^{\phi(p)} \equiv1 mod(p)\\ 對於任意b\ge\phi(p),有a^b\equiv a^{b~mod~\phi(p)+\phi(p)}\\ b<\phi(p),a^b\equiv a^{b~mod~\phi(n)}(mod~p)\\ a和p可以不互質,這裡的指數顯然滿足1\\ \]
P2606 ZJOI2010 排列計數
由題意\(p_i>p\lfloor i/2\rfloor\),\(p_i>p_{2i}(l\le n/2)\) \(p_i>p_{2i+1}(i<n/2)\)
顯然是個小根堆,在樹中填寫數字
假設我們在\(i\)
\(f[i]={i-1\choose l}*f[l]*f[r]\)
#include<cstdio> #define maxn 1000005 #define int long long using namespace std; int a[maxn],n,f[maxn],jc[maxn],m; int qpow(int a,int b){ int ans = 1; while(b){ if(b & 1) ans = ans * a % m; b >>= 1; a = a * a % m; } return ans; } int ask(int k,int l){ if(k == 1||k == 0||k == 2) return 1; if(k == 3) return 2; int u = a[l<<1],v = a[l<<1|1]; if(f[u] == 0) f[u] = ask(u,l<<1); if(f[v] == 0) f[v] = ask(v,l<<1|1); return jc[k-1] * qpow(jc[u],m-2) % m * qpow(jc[k-1-u],m-2) % m * f[u] % m * f[v] % m; } signed main(){ scanf("%lld%lld",&n,&m); for(int i = 1;i <= n;i++){ int k = i; while(k) a[k] ++,k>>=1; } jc[0] = jc[1] = 1; for(int i = 2;i<=n;i++) jc[i] = jc[i-1] * i % m; printf("%lld",ask(n,1)); }
更妙的是
答案等價於求一個樹的拓撲排序數量
數字的大小關係可以看做是一條有向邊,這樣以每個位置當點,就可以把整個排列當做一張有向圖。而且題目保證有解,所以只一張有向無環圖。這樣子,我們就可以把排列計數的問題轉化為一個圖的拓撲排序計數問題
\[公式ans=\frac{n!}{\prod_{i=1}^ns[i]}~~~s[i]為子節點的數量\\ f[u]=\frac{(s[u]-1)!*\prod f[v]!}{\prod s[v]!}\\ \]