原题链接:
https://acm.hdu.edu.cn/showproblem.php?pid=4704
题目含义:
给出一个n,求出这个n能够分解出多少种子序列,每个子序列的和要等于n
比如,n=4,它的子序列有下面几种情况
由1个数字组成:4;
由2个数字组成:13 31 22;
由3个数字组成:112 121 211;
由4个数字组成:1111;
每次选择的数要小于等于n,选择由几个数组成也要小于等于n
解题思路:
我们能够观察到
∑
k
=
1
N
S
(
k
)
=
C
n
−
1
0
+
C
n
−
1
1
+
C
n
−
1
2
+
⋅
⋅
⋅
+
C
n
−
1
n
−
1
\sum_{k=1}^{N}S(k)=C_{n-1}^{0}+C_{n-1}^{1}+C_{n-1}^{2}+···+C_{n-1}^{n-1}
∑k=1NS(k)=Cn−10+Cn−11+Cn−12+⋅⋅⋅+Cn−1n−1
根据高中数学知识我们可以得到
上式
=
2
N
−
1
=2^{N-1}
=2N−1
此题就转换为了求
2
N
−
1
2^{N-1}
2N−1,观察数据范围
n
<
1
0
100000
n<10^{100000}
n<10100000,范围非常大,于是我们就会想到快速幂,但是n还是很大,即使是快速幂也会超时,那么就要结合费马定理来缩小范围。
费马定理:
存在a,p且gcd(a,p)=1,则有 a p − 1 ≡ 1 ( m o d p ) a^{p-1}\equiv1(\mod p) ap−1≡1(modp),即 a p − 1 % p = 1 a^{p-1}\%p=1 ap−1%p=1
证明请看:
<>
优化思路一:
a
n
=
a
n
−
(
p
−
1
)
∗
a
p
−
1
a^{n}=a^{n-(p-1)}*a^{p-1}
an=an−(p−1)∗ap−1
两边同余一个
p
p
p,得到:
a
n
≡
a
n
−
(
p
−
1
)
m
o
d
p
a^{n}\equiv a^{n-(p-1)} modp
an≡an−(p−1)modp
我们就把
n
n
n缩小到
n
−
(
p
−
1
)
n-(p-1)
n−(p−1),但是
n
>
>
p
−
1
n>>p-1
n>>p−1,优化很鸡肋
思路二:
令
k
=
n
/
(
p
−
1
)
k=n/(p-1)
k=n/(p−1)
n
=
k
∗
(
p
−
1
)
+
n
m
o
d
(
p
−
1
)
n=k*(p-1)+n mod(p-1)
n=k∗(p−1)+nmod(p−1)
(带余除法,k是对应的商,n mod (p-1)是对应的余数)
a
n
=
a
k
∗
(
p
−
1
)
∗
a
n
m
o
d
(
p
−
1
)
a^n=a^{k*(p-1)}*a^{n mod (p-1)}
an=ak∗(p−1)∗anmod(p−1)
(
a
k
∗
(
p
−
1
)
≡
1
m
o
d
p
a^{k*(p-1)}\equiv1 modp
ak∗(p−1)≡1modp,由费马小定理可得)
上式可得:
a
n
≡
a
n
m
o
d
(
p
−
1
)
m
o
d
p
a^n\equiv a^{n mod (p-1)} mod p
an≡anmod(p−1)modp
两边同时取余
p
p
p
a
n
%
p
=
a
n
m
o
d
(
p
−
1
)
%
p
a^n\%p=a^{nmod(p-1)}\%p
an%p=anmod(p−1)%p
优化就完成了,我们只需要计算
n
m
o
d
(
p
−
1
)
n mod (p-1)
nmod(p−1)次方就行了
完整代码:
#include <iostream>
#include <string>
using namespace std;
typedef long long ll;
const int MOD = 1e9 + 7;
const int PHI = MOD - 1;
int solve(string num, int mod) {
long long res = 0;
for (char c : num) {
res = (res * 10 + (c - '0')) % mod;
}
return res;
}
ll qmi(ll a, ll b, int mod) {
ll res = 1;
while (b > 0) {
if (b & 1) res = res * a % mod;
a = a * a % mod;
b >>= 1;
}
return res;
}
int main() {
string s;
while(cin >> s){
int n = solve(s, PHI);
n = (n - 1 + PHI) % PHI;
ll ans = qmi(2, n, MOD);
cout << ans << endl;
}
return 0;
}