【ZJOI2010】排列计数
题面
https://www.luogu.org/problem/P2606
题解
数列按广度优先搜索序搜索,变成一个堆。所以形态是给定的。
只需记忆化搜索就可以了,复杂度$O(logn)$。顺序递推亦可。
对于阶乘超过$p$的情况,我们把$p$的次幂记下来,算的时候直接减去。$aysn$说这是卢卡斯定理,我只能$orz$了。
#include<cstdio> #include<cstring> #include<iostream> #include<algorithm> #define ri register int #define N 1000500 #define LL long long using namespace std; int n,p; int jc[N],inv[N],jcp[N],invp[N]; int C1(int a,int b) { if (a==b || a==0) return 0; return jcp[b]+invp[a]+invp[b-a]; } int C2(int a,int b) { if (a==b || a==0) return 1; return ((jc[b]*1LL*inv[a])%p*1LL*inv[b-a])%p; } LL solve1(int x) { if (x==1 || x==0) return 0; for (ri i=20;i>=0;i--) if (x&(1<<i)) { if (x&(1<<(i-1))) return solve1((1<<i)-1)+solve1(x-(1<<i))+C1((1<<i)-1,x-1); else return solve1(x-(1<<(i-1)))+solve1((1<<(i-1))-1)+C1((1<<(i-1))-1,x-1); } } LL solve2(int x) { if (x==1 || x==0) return 1; for (ri i=20;i>=0;i--) if (x&(1<<i)) { if (x&(1<<(i-1))) return ((solve2((1<<i)-1)*solve2(x^(1<<i)))%p*C2((1<<i)-1,x-1))%p; else return (solve2(x-(1<<(i-1)))*solve2((1<<(i-1))-1)%p*C2((1<<(i-1))-1,x-1))%p; } } int pow(int a,int b) { int ret=1; for (;b;b>>=1,a=(a*1LL*a)%p) if (b&1) ret=(ret*1LL*a)%p; return ret; } int count(int x) { int ret=0; while (x%p==0) x/=p,ret++; return ret; } int main() { cin>>n>>p; jc[0]=jc[1]=1; for (ri i=2;i<N;i++) if (i%p) { jc[i]=(jc[i-1]*1LL*i)%p; jcp[i]=jcp[i-1]; } else { jc[i]=jc[i-1]; jcp[i]=jcp[i-1]+count(i); } inv[N-1]=pow(jc[N-1],p-2); invp[N-1]=-jcp[N-1]; for (ri i=N-2;i>=1;i--) if ((i+1)%p) { inv[i]=(inv[i+1]*1LL*(i+1))%p; invp[i]=invp[i+1]; } else { inv[i]=inv[i+1]; invp[i]=invp[i+1]+count(i+1); } if (solve1(n)==0) cout<<solve2(n)<<endl; else cout<<0<<endl; return 0; }