- 题目描述 精通程序设计的 Applese 叕写了一个游戏。 在这个游戏中,有一个 n 行 m 列的方阵。现在它要为这个方阵涂上黑白两种颜色。规定左右相邻两格的颜色不能相同。请你帮它统计一下有多少种涂色的方法。由于答案很大,你需要将答案对 109+7 取模。
- 输入描述: 仅一行两个正整数 n, m,表示方阵的大小。
- 输出描述: 输出一个正整数,表示方案数对 109+7 取模。
- 示例1 输入 1 1 输出 2
- 示例2 输入 2 2 输出 4
- 备注:1≤n,m≤10100000
首先我们先看第一列上面的情况,每一个格子有两种(黑色和白色)选择,那么n个格子就是2n种情况,我们就得到第一列有2n种情况,题目又要求相邻两个两格的颜色不能相同,所以第一列一旦确定以后,后面的方格所放的颜色就确定了。最终我们得到的方案数就是2n。即使是我们知道了方案数那么解决起来也有一些困难,我们可以看到,这里的n给的很大,即便我们用快速幂去解也是不行的,下面我介绍两种方法来解决这个问题。
- 费马小定理
如果p是素数,a是正整数,且gcd(a,p)=1,则有ap-1≡1(mod p)
对于ap-1≡1(mod p)有ap-1≡a0≡1(mod p),说明ax存在循环节,并且此循环节的长度为(p-1),所以可以得到ax%p=ax%(p-1)%p.
大数取余:大数取余我们可以手动模拟算法,过程如下:
#include<cstdio>
#include<cstdlib>
#include<iostream>
#include<cmath>
#include<cstring>
#define pi 3.1415926
#define mod 1000000007
using namespace std;
typedef long long LL;
const int Max_n=100005;
char a[Max_n],str[Max_n];
LL Qpow_mod(){//大数取余
LL ans=0;
int n=strlen(a);
for(int i=0;i<n;i++)
ans=(ans*10+(a[i]-'0'))%(mod-1);
return ans;
}
LL Qpow(LL a,LL b){
LL ans=1;
LL res=a%mod;
while(b){
if(b&1) ans=(ans*res)%mod;
res=(res*res)%mod;
b>>=1;
}
return ans;
}
int main(){
scanf("%s%s",a,str);
LL b=Qpow_mod();
printf("%lld\n",Qpow(2,b));
return 0;
}
- 欧拉定理(也称费马-欧拉定理)
如果n和a是互素的正整数,则aφ(n)≡1(mod n)
基于对上面费马小定理的循环节,我们可以得到ax%n≡ax%φ(n)%n欧拉φ函数φ(n):设n是一个正整数。欧拉φ函数φ(n)是不超过n且与n互素的正整数的个数。
定理:正整数m可以表示为素幂因子分解m=p1k1 * p2k2 * … * prkr,其中p1,p2…pr是不同的素数,则有φ(m)=φ(p1k1) * φ(p2k2)… * φ(prkr)。若p是一个素数,且k是正整数,则有φ(pk)=pk-pk-1
根据上述定理,我们可以推出来φ(n)。n=p1k1 * p2k2 * … * pmkm,则有φ(m)=φ(p1k1) * φ(p2k2)… * φ(prkr),φ(pk)=pk-pk-1,即 φ(pk)=pk-1 * (p-1)。
#include<cstdio>
#include<cstdlib>
#include<iostream>
#include<cmath>
#include<cstring>
#include<string>
#define pi 3.1415926
#define mod 1000000007
#include<algorithm>
using namespace std;
typedef long long LL;
const int Max_n=100005;//sqrt(mod)
int prime[Max_n],is_prime[Max_n],j;
char a[Max_n],str[Max_n];
void f(){//埃式筛法(之前的博文中讲过这个问题)
for(int i=2;i<=Max_n;i++) is_prime[i]=1;
for(int i=2;i<=sqrt(Max_n);i++){
if(is_prime[i]){
for(int j=i*i;j<=Max_n;j+=i)
is_prime[j]=0;
}
}
int j=1;
for(int i=2;i<=Max_n;i++)
if(is_prime[i])
prime[j++]=i;
}
LL phi(LL x){//欧拉函数φ(x)
LL ans=1;//最终结果
for(int i=1;i<j;i++){
if(x%prime[i]==0){//存在素因子
int num=0;//当前素因子的个数
while(x%prime[i]==0){
j++;//如果是当前还是的素数,数量+1 这里应该为num++;
x/=prime[i];
}
for(int k=1;k<num;k++) ans*=prime[i];//p^(k-1)
ans*=(x-1);//(p-1) 这里应该修改为prime[i]-1
if(x==1) break;//是1直接返回ans
}
}
if(x>1) ans*=(x-1);//x如果是一个素数,φ(x)=x-1;
return ans;
}
LL Qpow(LL a,LL b){//快速幂
LL ans=1;
LL res=a%mod;
while(b){
if(b&1) ans=(ans*res)%mod;
res=(res*res)%mod;
b>>=1;
}
return ans;
}
int main(){
f();
LL mmod=phi(mod); // φ(mod)
scanf("%s%s",a,str);
//大数取余
LL ans=0;
int n=strlen(a);
for(int i=0;i<n;i++)// n% φ(mod)
ans=(ans*10+(a[i]-'0'))%mmod;
printf("%lld\n",Qpow(2,ans));//2^(n%φ(mod))%mod
return 0;
}