蓝桥杯C++大学B组一个月冲刺记录2024/3/8

文章讲述了关于奶牛基因学问题的编程挑战,涉及连续奶牛照片的计数、双指针优化和子串分值计算,以及如何通过贡献度策略解决特定问题。作者分享了使用双指针方法和贡献度思想来解决题目中的计数问题,并提到一种TLE(时间超出限制)的情况。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

蓝桥杯C++大学B组一个月冲刺记录2024/3/8

规则: 每日三题

1.孤独的照片

Farmer John 最近购入了 N头新的奶牛,每头奶牛的品种是更赛牛(Guernsey)或荷斯坦牛(Holstein)之一。
奶牛目前排成一排,Farmer John 想要为每个连续不少于三头奶牛的序列拍摄一张照片。
然而,他不想拍摄这样的照片,其中只有一头牛的品种是更赛牛,或者只有一头牛的品种是荷斯坦牛——他认为这头奇特的牛会感到孤立和不自然。
在为每个连续不少于三头奶牛的序列拍摄了一张照片后,他把所有「孤独的」照片,即其中只有一头更赛牛或荷斯坦奶牛的照片,都扔掉了。
给定奶牛的排列方式,请帮助 Farmer John 求出他会扔掉多少张孤独的照片。
如果两张照片以不同位置的奶牛开始或结束,则认为它们是不同的。

贡献度
双指针应该是被卡常数了导致TLE,有一组数据过不了(11/12)
但这个双指针已经接近O(n2)的做法了。但属于我看到这个题的第一想法,所以记录一下。

贡献度的方法可以这样思考:
当处于 i 位置的G。且左边最近的G的下标是 l ,右边最近的下标为 r 。那么仅有第 i 位置的 G 参与的序列就有 (r - i) * (i - l)个。
那么对每一个 G 和 H 进行如此操作,答案就是操作总数。

双指针做法

#include<iostream>

using namespace std;

const int M = 5e5 + 10;

char st[M];
int p[30];
int temp[30];
int n;
int ans = 0;
int l,r;
int main(){
    cin >> n;
    cin >> st;
    for(int l = 0,r = 0;r < n; ++r){

        p[st[r] - 'A']++;

        while(p['G' - 'A'] > 1 && p['H' - 'A'] > 1){
            p[st[l] - 'A'] --;
            l++;
        }

        if (r - l + 1 >= 3){
            int ll = l;

            temp['G' - 'A'] = p['G' - 'A'];
            temp['H' - 'A'] = p['H' - 'A'];

            while((temp['G' - 'A'] == 1 || temp['H' - 'A'] == 1)&&r - ll  + 1 >= 3){
              temp[st[ll] - 'A'] --;
              ll++;
            }
            ans += ll - l;
        }
    }
    cout << ans << endl;
    return 0;
}

贡献度做法

#include<iostream>

using namespace std;
typedef long long LL;
const int M = 5e5 + 10;

char st[M];

LL ans;

int l[M],r[M],p;

int n;

LL work(char t){
    LL tot = 0;

    p = 0;

    for(int i =  1;i <= n;++i){
        if(st[i] == t){
            l[i] = p;
            p = i;
        }
    }

    p = n + 1;

    for(int i = n;i >= 1;--i){
        if(st[i] == t){
           r[i] = p;
           p = i;
        }
    }

    for(int i = 1;i <= n; ++i){
        if(st[i] == t){
            
            tot += (LL)(i - l[i] - 1) * (r[i] - i - 1) + max(0 , i - l[i] - 2) + max(0,r[i] - i - 2);
        }
    }

    return tot;
}

int main(){

    cin >> n;
    
    cin >> (st + 1);
    
    ans = work('G') + work('H');
    
    cout << ans << endl;
    return 0;


}

2.子串分值

对于一个字符串 S,我们定义 S的分值 f(S)为 S中恰好出现一次的字符个数。
例如 f(“aba”)=1,f(“abc”)=3, f(“aaa”)=0。
现在给定一个字符串 S[0…n−1](长度为 n),请你计算对于所有 S 的非空子串 Si…j
, f(S[i…j]) 的和是多少。

贡献度
思路与第一题类似,只不过要求的字母的个数更多。
思考:为什么tot的赋值与第一题不同(第一题有数量>=3的限制)

#include<iostream>
#include<algorithm>
#include<cstring>

using namespace std;

const int M = 1e5 + 10;
typedef long long LL;

char st[M];
int l[M],r[M];
int p[30];//上一个字符存放的位置

LL work(int n){
    
    LL tot = 0;

    for(int i = 1;i <= n; ++i){
        l[i] = p[st[i] - 'a'];
        p[st[i] - 'a'] = i;
    }

    for(int i = 0;i <= 30;++i) p[i] = n + 1;

    for(int i = n;i >= 1; --i){
        r[i] = p[st[i] - 'a'];
        p[st[i] - 'a'] = i;
    }

    for(int i = 1;i <= n; ++i){
        tot += (LL)(r[i] - i) * (i - l[i]); 
    }

    
    return tot;
}
int main(){

    cin >> (st+1);
    
    int n = strlen(st + 1);

    LL ans = work(n);

    cout << ans << endl;

    return 0;

}

3.牛的基因学

题目太复杂,不贴
思维题+贡献度
可以发现对于t中的每个元素 在左移0 ~ n - 1的全局操作中 其实就是和s中的每一个字符匹配一次
因此若t中的某个字符为’A’ 那么其对结果的贡献即为s中含有的’A’的数量
因此为了使得结果最大 t中的元素因尽可能为s中个数最多的元素

问题即转换为 找到s中个数最多的元素 让t中的每个元素都赋值为该元素即可
当s中最多的元素个数为1个时 只能将所有t中的元素都赋值为该元素 答案为1^n
当s中最多的元素个数为2个时 t中的每个元素都有两种选择 任选其一均可 答案为2^n
当s中最多的元素个数为3个时 t中的每个元素都有三种选择 任选其一均可 答案为3^n

#include<iostream>

using namespace std;

const int M = 1e5 + 10;
const int mod = 1e9+7;

typedef long long LL;

char st[M];
int p[30];

int mx,t,n;
int main(){
    
    cin >> n;
    cin >> st;

    for(int i = 0; i < n; ++i){
        p[st[i] - 'A'] ++;

        if(p[st[i]- 'A'] > mx){
            mx = p[st[i] - 'A'];
            t = 1;
        }
        else if(p[st[i]- 'A'] == mx){
            t ++;
        }
    }

    LL res = 1;
    for(int i = 1;i <= n; ++i){
        res = (LL) res * t % mod;
    }

    cout << res << endl;

    return 0;


}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值