CCF有趣的数(快速幂,递推)

来源:互联网 发布:手游代练软件 编辑:程序博客网 时间:2024/05/01 01:26

问题描述

  我们把一个数称为有趣的,当且仅当:
  1. 它的数字只包含0, 1, 2, 3,且这四个数字都出现过至少一次。
  2. 所有的0都出现在所有的1之前,而所有的2都出现在所有的3之前。
  3. 最高位数字不为0。
  因此,符合我们定义的最小的有趣的数是2013。除此以外,4位的有趣的数还有两个:2031和2301。
  请计算恰好有n位的有趣的数的个数。由于答案可能非常大,只需要输出答案除以1000000007的余数。
输入格式
  输入只有一行,包括恰好一个正整数n (4 ≤ n ≤ 1000)。
输出格式
  输出只有一行,包括恰好n 位的整数中有趣的数的个数除以1000000007的余数。
样例输入
4
样例输出
3

题解

一开始看这题真的是毫无头绪,后来尝试从一些很简单的问题入手,发现这个数只能以2开头,以1或3结尾,情况以下子少了很多,于是看看能不能递推,思路如下:
- 设长度为n(n>=4)的有趣的数个数为a[n]
- 考察a[n+1],
- 若这个数以1结尾
- 若前n位有0123,该情况数是a[n]
- 若前n位没有1,设第一个3出现的位数是k(1≤k≤n-1,从左往右从1开始),该种情况方法数为2k1×2nk11=2n21,于是总的方法数为(n-1)(2n21)
- 若这个数以3结尾
- 若前n位有0123,该情况数是a[n]
- 若前n位没有3,情况略有不同,设第一个1出现的位数是k(2≤k≤n-1),该种情况方法数为(2k11)×2nk1=2n12nk1,于是总的方法数为n1k=2(2n12nk1)=(n3)×2n2+1
- 综上,a[n+1]=2a[n]+(n-2)*2n1-n+2
- 但是直接这么算,n稍微大一点就会溢出,因此要随时取模

/*a[n+1]=2a[n]+(n-2)2^(n-1)-n+2*/#include<stdio.h>#define m 1000000007long long Pow(int n)/*计2^n mod m*/{    if (n==1)        return 2;    long long half;    half=Pow(n/2);    if ((n&1)==0)        return (half*half)%m;    else        return (((half*half)%m)*2)%m;}int main(void){    long long a=3;    int i,n;    scanf("%d",&n);    for (i=4;i<n;i++)        a=(a*2+(i-2)*Pow(i-1)-i+2)%m;/*可以保证在取余之前不会溢出,如果是int的话,(i-2)*Pow(i-1)可能会溢出*/    printf("%lld",a);    return 0;}
0 0