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

1 篇文章 0 订阅
1 篇文章 0 订阅

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
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值