2017-03-04 set—难解的集合

来源:互联网 发布:a byte of python csdn 编辑:程序博客网 时间:2024/04/29 10:59

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;}
1 0