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(n1))=M(F(n)F(n1))
( 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 2m1 的求和答案怎么算出来的呢?
我们需要注意到, 0 —— 2 m − 1 0——2^m-1 0——2m1中,
二进制数 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=02m1f(g(i))=F0(T0+T1)m=F0(Cm0T00T1m+Cm1T01T1m1+Cm2T02T1m2+......)
由于 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;
}
目前尚未找到关于2024年河北省CCPC的具体官方信息。然而,可以基于以往的比情况推测一些可能的内容。 ### 参准备与注意事项 通常情况下,CCPC(中国大学生程序设计竞)会在每年的秋季举行区域。参者需注意以下几点: 1. **团队协作** 每支队伍由三名队员组成,分工合作至关重要。例如,在之前的比中,有队伍因签到题失误而导致时间浪费和罚时增加[^1]。因此,合理分配题目并快速定位错误是成功的关键。 2. **算法模板储备** 动态规划(DP)、图论、字符串处理等常见算法应熟练掌握。比如动态规划的一个经典实现如下所示: ```cpp #include <bits/stdc++.h> using namespace std; int k, n, m, dp[1005][1005]; int a[1005], b[1005]; int pa[1005] = {0}, pb[1005] = {0}; queue<int> q[1005]; int main() { int ak; cin >> ak; while (ak--) { for (int i = 1; i <= n; i++) while (!q[i].empty()) q[i].pop(); scanf("%d%d%d", &k, &n, &m); memset(dp, 0, sizeof(dp)); memset(pa, 0, sizeof(pa)); memset(pb, 0, sizeof(pb)); for (int i = 1; i <= n; i++) cin >> a[i]; for (int i = 1; i <= m; i++) cin >> b[i]; for (int i = 1; i <= n; i++) { q[a[i]].push(i); if (q[a[i]].size() == k) { pa[i] = q[a[i]].front(); q[a[i]].pop(); } } for (int i = 1; i <= n; i++) while (!q[i].empty()) q[i].pop(); for (int i = 1; i <= m; i++) { q[b[i]].push(i); if (q[b[i]].size() == k) { pb[i] = q[b[i]].front(); q[b[i]].pop(); } } for (int i = 1; i <= n; i++) { for (int j = 1; j <= m; j++) { dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]); if (a[i] == b[j] && pa[i] != 0 && pb[j] != 0) dp[i][j] = dp[pa[i] - 1][pb[j] - 1] + k; } } printf("%d\n", dp[n][m]); } return 0; } ``` 3. **模拟训练** 定期参加ACM/ICPC风格的在线比或学校内部模拟有助于提升实战能力。通过解决类似矩形面积计算等问题来增强几何算法的理解[^3]。 ### 事安排预测 虽然具体日期未公布,但根据往年惯例,建议关注以下几个方面: - 报名截止时间和方式; - 初步计划中的比地点及日程表; - 是否存在线上预选环节。 #### 提醒事项 务必留意官网公告以及教练通知,及时调整复习策略以应对可能出现的新变化。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值