蓝桥杯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;
}