The 2024 CCPC National Invitational Contest (Changchun),第17届吉林省赛 C
补题链接
题解是什么意思呢,首先我们需要知道的是,斐波那契数列可以用矩阵快速幂和矩阵乘法求解
(
F
(
n
+
1
)
F
(
n
)
)
=
(
1
1
1
0
)
(
F
(
n
)
F
(
n
−
1
)
)
=
M
(
F
(
n
)
F
(
n
−
1
)
)
\begin{pmatrix}F(n+1) \\ F(n)\end{pmatrix} = \begin{pmatrix}1 & 1 \\ 1 & 0\end{pmatrix}\begin{pmatrix}F(n) \\ F(n-1)\end{pmatrix}= M \begin{pmatrix}F(n) \\ F(n-1)\end{pmatrix}
(F(n+1)F(n))=(1110)(F(n)F(n−1))=M(F(n)F(n−1))
(
F
(
n
+
1
)
F
(
n
)
)
=
M
n
(
1
0
)
\begin{pmatrix}F(n+1) \\ F(n)\end{pmatrix} = M^n \begin{pmatrix}1 \\ 0\end{pmatrix}
(F(n+1)F(n))=Mn(10)
题解横着放也是一样的,根绝题解给出的例子,手模一下发现是对的,如何去理解?
T
0
T0
T0是一个单位阵
E
E
E,
T
1
T1
T1和我上面写的矩阵相似,
E
E
E和任何矩阵
A
A
A相乘结果都是A,所以可以理解为
0
0
0 的贡献是
E
E
E ,也就是没有,二进制
1
1
1 的贡献则是
T
1
(
M
)
T1(M)
T1(M) 让斐波那契数列往下递推一次
然后题解那个从0 到
2
m
−
1
2^m-1
2m−1 的求和答案怎么算出来的呢?
我们需要注意到,
0
——
2
m
−
1
0——2^m-1
0——2m−1中,
二进制数
1
1
1 的个数为
0
0
0 的数有
C
n
0
C_n^0
Cn0 个
二进制数
1
1
1 的个数为
1
1
1 的数有
C
n
1
C_n^1
Cn1 个
二进制数
1
1
1 的个数为
2
2
2 的数有
C
n
2
C_n^2
Cn2 个
…
我们通过打表可以验证其正确性,我们把那个题解的式子展开一下
∑
i
=
0
2
m
−
1
f
(
g
(
i
)
)
=
F
0
⋅
(
T
0
+
T
1
)
m
=
F
0
⋅
(
C
m
0
⋅
T
0
0
⋅
T
1
m
+
C
m
1
⋅
T
0
1
⋅
T
1
m
−
1
+
C
m
2
⋅
T
0
2
⋅
T
1
m
−
2
+
.
.
.
.
.
.
)
\sum_{i=0}^{2^m-1} f(g(i))=F_0\cdot(T_0+T_1)^m=F_0\cdot(C_m^0\cdot T_0^0\cdot T_1^m+C_m^1\cdot T_0^1\cdot T_1^{m-1}+C_m^2\cdot T_0^2\cdot T_1^{m-2}+......)
∑i=02m−1f(g(i))=F0⋅(T0+T1)m=F0⋅(Cm0⋅T00⋅T1m+Cm1⋅T01⋅T1m−1+Cm2⋅T02⋅T1m−2+......)
由于
T
0
T_0
T0 是一个单位阵,所以可以直接拿掉,那么刚好和注意到的和前面的计算贡献的方法对应起来,那么按照题解说的固定前缀即可算出最终答案
先贴一份题解代码
#pragma GCC optimize(3,"Ofast","inline")
#include<bits/stdc++.h>
using namespace std;
using i64 = long long;
using i128 = __int128;
constexpr int mod = 1e9+7,maxn = 1e7+2;
string s;
array<int,2> F0={{0,1}};
array<array<int,2>,2> E ={{{1,0},{0,1}}};
array<array<int,2>,2> T1={{{0,1},{1,1}}};
array<array<int,2>,2> T ={{{1,1},{1,2}}};
array<array<int,2>,2> t[maxn];
array<array<int,2>,2> pre[maxn];
array<array<int,2>,2> mul(array<array<int,2>,2> a,array<array<int,2>,2> b) {
array<array<int,2>,2> res;
res[0][0]=(1LL*a[0][0]*b[0][0]+1LL*a[0][1]*b[1][0])%mod;
res[0][1]=(1LL*a[0][0]*b[0][1]+1LL*a[0][1]*b[1][1])%mod;
res[1][0]=(1LL*a[1][0]*b[0][0]+1LL*a[1][1]*b[1][0])%mod;
res[1][1]=(1LL*a[1][0]*b[0][1]+1LL*a[1][1]*b[1][1])%mod;
return res;
}
array<int,2> Mul(array<int,2> a,array<array<int,2>,2> b){
array<int,2>res;
res[0]=(1LL*a[0]*b[0][0]+1LL*a[1]*b[1][0])%mod;
res[1]=(1LL*a[0]*b[0][1]+1LL*a[1]*b[1][1])%mod;
return res;
}
signed main(){
ios::sync_with_stdio(0);cin.tie(0);
cin>>s;
int n = s.size();
pre[0]=t[0]=E;
t[1]=T;
pre[1]=T1;
for(int i = 2;i<=n+1;++i){
t[i]=mul(t[i-1],T);
pre[i]=mul(pre[i-1],T1);
}
i64 ans = 0;
int pre1=0;
for(int i = 0;i<n;++i){
if(s[i]=='0'){
continue;
}else{
ans = (ans+Mul(F0,mul(pre[pre1],t[n-i-1]))[0])%mod;
pre1++;
}
}
if(s.back()=='1') ans = (ans+Mul(F0,pre[pre1])[0])%mod;
else ans = (ans+Mul(F0,pre[pre1])[0])%mod;
cout<<ans<<'\n';
return 0;
}
再贴一份队友的代码,队友太会DP了
#include <bits/stdc++.h>
using namespace std;
using i64 = long long;
using i128 = __int128;
int fb[10000010], n;
string s;
constexpr int mod = 1e9 + 7;
int dp[10000000 + 10][2][2];
void solve()
{
cin >> s;
memset(dp, -1, sizeof dp);
n = s.size();
dp[n - 1][0][0] = 1;
dp[n - 1][0][1] = 0;
if (s.back() == '1'){
dp[n - 1][1][0] = 1;
dp[n - 1][1][1] = 0;
}
else{
dp[n - 1][1][0] = 0;
dp[n - 1][1][1] = 0;
}
for (int pos = n - 2; pos >= 0; --pos){
for (int limit = 0; limit <= 1; ++limit){
if (limit){
if (s[pos] == '0'){
dp[pos][limit][0] = dp[pos + 1][limit][0];
dp[pos][limit][1] = dp[pos + 1][limit][1];
}
else{
dp[pos][limit][0] = (1ll * dp[pos + 1][0][0] + 1ll * dp[pos + 1][1][0] + 1ll * dp[pos + 1][1][1] + 1) % mod;//这一位取0+对于这一位来说f(n-1)+对于这一位来说f(n-2)+自己这一位是1的贡献
dp[pos][limit][1] = (1ll * dp[pos + 1][1][0] + 1ll * dp[pos + 1][0][1]) % mod;//这一位取1+这一位取0
}
}
else{
dp[pos][limit][0] = (2LL * dp[pos + 1][0][0] + 1ll * dp[pos + 1][0][1] + 1) % mod;
dp[pos][limit][1] = (1ll * dp[pos + 1][0][0] + 1ll * dp[pos + 1][0][1]) % mod;
}
}
}
cout << dp[0][1][0]<< "\n";
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);
int t = 1;
fb[1] = fb[2] = 1;
for (int i = 3; i <= 1e7; i++)
fb[i] = (fb[i - 1] + fb[i - 2]) % mod;
while (t--)
solve();
return 0;
}