xdoj 1067 P-Function

这是一道数学题,意思是长度n的置换,问将该置换嵌套k次后不动的方案数。

因为置换可以分解为轮换,那么不动的方案轮换的大小肯定是k的因子。枚举k的因子d,下一步要确定这样长度d轮换有多少个,其余n-d个只需要递推求出。长度为d的轮换不是想当然的d个,也不是phi(d)个。想象这样的轮换构成环,第一个元素的指向有n-1种选择,第二个元素有n-2种选择,……,总个数为A(n-1,d-1)个。

再解释一下,算环的时候除以n而不是除以d,这是因为防止递推剩下n-d的时候同样大小的环被计重。

  1. /*
  2. Author: fffasttime
  3. Date: 
  4. */
  5. #include <bits/stdc++.h>
  6. using namespace std;
  7. typedef long long ll;
  8. #define inc(i,n) for (int i=0;i<n;i++)
  9. const int N=50010;
  10. const ll P=1e9+7;
  11.  
  12. ll fac[N],ifac[N],dp[N];
  13.  
  14. ll qpow(ll a, ll x){
  15. 	ll ans=1;
  16. 	for (;x;a=a*a%P,x>>=1)
  17. 		if (x&1) ans=ans*a%P;
  18. 	return ans;
  19. }
  20. ll A(ll n, ll m){if (m==0) return 1;return fac[n]*ifac[n-m]%P;}
  21.  
  22. int d[N],dc;
  23.  
  24. int main(){
  25. 	fac[0]=ifac[0]=1;
  26. 	for (int i=1;i<N;i++) fac[i]=(fac[i-1]*i)%P;
  27. 	for (int i=1;i<N;i++) ifac[i]=qpow(fac[i],P-2);
  28. 	int T; cin>>T;
  29. 	while (T--){
  30. 		ll n,k; cin>>n>>k;
  31. 		int mf=sqrt(k);
  32. 		dc=0;
  33. 		memset(dp,0,sizeof dp);
  34. 		for (int i=1;i<=mf;i++) if (k%i==0){
  35. 			d[dc++]=i;
  36. 			if (i*i!=k) d[dc++]=k/i;
  37. 		}
  38. 		dp[0]=1;
  39. 		for (int i=1;i<=n;i++){
  40. 			for (int j=0;j<dc;j++)
  41. 				if (i-d[j]>=0)
  42. 					dp[i]+=dp[i-d[j]]*A(i-1,d[j]-1)%P,dp[i]%=P;
  43. 		}
  44. 		cout<<dp[n]<<'\n';
  45. 	}
  46. 	return 0;
  47. }

发表评论

您的电子邮箱地址不会被公开。 必填项已用*标注