Description
定义集合 A={1,2,3…,n} , 现在有一个 A->A 的函数 f(x) , 也就是说这个函数的 定义域是 A, 值域是 A 的非空子集。
现在请你找出有多少种可能的 f(x) , 使得对于所有 x0(x0∈ A) , f(f(x0)) 都为同 一个值。 请将方案数 mod 10^9+7 后输出 。
Input
第一行一个正整数 n, 表示集合 A。
Output
第一行一个数 Ans, 表示 f(x) 可能方案数。
Sample Input
3
Sample Output
9
样例解释
所有满足条件的 f() 如下(三个数分别表示 f(1) 、 f(2) 、 f(3) ):
{1 1 1} {1 1 2} {1 3 1}
{2 2 2} {2 2 1 } {2 3 3}
{3 3 3} {3 1 3} {3 2 2}
一共有 9 种不同的 f() 。
数据范围
对于 20%的数据, n≤8。
对于 40%的数据, n≤20。
对于 60%的数据, n≤1500。
对于 100%的数据, n≤10^5。
solution
组合数,乘法逆元,费马小定理,快速幂…..这些还是理理清楚比较好
用快速幂实现费马小定理,用费马小定理求逆元,用逆元求组合数,用组合数求在n-1个数中取L-1个数的方案数,用快速幂求剩余n-L个数在L-1个数中取的方案数,两者乘起来加上ans,最后ans*n,因为p有n种可能。
设最后都收束为p,显然f(p)=p。设有L个f(x)=p,除了p还要任选L-1个数,剩下n-L都要在这L-1个位置中任取。枚举L,复杂度为O(n);
其实最暴力的n的n次方的打表还是有20分的呢
Code
#include <cstdio>
#include <algorithm>
using namespace std;
typedef long long int64;
const int S = 100010;
const int mo = (int)1e9 + 7;
int n, fac[S];
int c1, c2, ans;
int ksm(int a, int b)//快速幂求a的b次方并返回逆元值
{
if (a < 0 || b < 0) return 0;
int w = 1;
for (; b; b >>= 1, a = (int64)a * a % mo)
if (b & 1) w = (int64)w * a % mo;
return w;
}
/*
这个是错误的快速幂
int ksm(int a,int b)
{
if(a<0||b<0) return 0;
int m=1;
b>>=1;
while(b)
{
if(b&1) m=(int64)m*a%mo;
a=a*a%mo;
b>>=1;
}
return m;
}*/
int inv(int x)//用费马小定理求逆元
{
return ksm(x,mo-2);
}
int main()
{
freopen("set.in" , "r", stdin);
freopen("set.out", "w", stdout);
scanf("%d",&n);
fac[0]=1;
for(int i=1;i<=n;i++)
fac[i]=(int64)fac[i-1]*i%mo;//求阶乘
for(int L=1;L<=n;L++)
{
c1=(int64)fac[n-1]*inv(fac[n-L])%mo*inv(fac[L-1])%mo;//在n-1中选取n-L个数的方案数
c2=(int64)ksm(n-L,L-1);//快速幂求n-L的L-1次方,即剩余的n-L个数有多少种选择方法
ans+=(int64)c1*c2%mo;ans%=mo;
}
ans=(int64)ans*n%mo;//总共第一个数可以取n种
printf("%d\n",ans);
return 0;
}