【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;
}

 

上一篇:局部加权回归实例


下一篇:求逆元的四种算法(拓欧费马小线性推欧拉)