【Ad-hoc】JSCPC 2022 L. Collecting Diamonds
题目描述
给定一个由 A,B,C 构成的字符串,每次你可以进行操作:
-
选择下标
,使得 。 -
如果
是奇数,删掉 A,C ;否则删掉 B。 -
更新每个字符的下标。
求最多能做多少次操作。
算法描述
这题关键在于一步步推出足以支持贪心的性质。
首先,我们发现以每个 B 为中心,左A右C 进行拓展,我们发现每个 B 之间拓展的区域互不干扰。
我们又发现,删掉 B 以后,这个中心就废了。
我们还发现,删 AC 只会改变自己的状态(AC
所以一个形如 AAAAABCCCCC 这样的东西一定是先把状态调到 AC,删一次,通过前面的东西调到 AC,再删一次...这样删完的。
我们并不关注这个时间顺序,所以我们发现我们需要最大化对后缀调整的次数,为后面的长串争取机会。
导出以下算法:
从左往右扫描,记当前区域AAA..BCCC..嵌套 A,C 的层数是 tmp,前面争取到的最大翻转次数是 rev。
-
tmp = 1
-
当前状态为 AC 并且 rev = 0
说明这段不能为后面贡献翻转次数。
-
否则
如果状态为 B 或是 rev > 0,代表状态都可以调整到 B,删除后可以为后面做出一次贡献,rev++
-
-
tmp > 1
-
状态是 AC
可以删除一次变成 B,B的情况见下面。
-
状态是 B
首先利用前面翻转一次,然后删一次 AC...重复过程,要么最后消耗完,要么翻转次数不够,无论怎么样都可以以删除 B 结束,做出一次翻转贡献,rev++,对 ans 的贡献是
。
-
注意以上过程删一次对答案贡献 1。
这样就完成了讨论,时间复杂度
#include<bits/stdc++.h>
using namespace std;
const int N = 2e5 + 5;
char s[N];
int ans = 0,n,rev = 0;
int main()
{
scanf("%s",s + 1);
n = strlen(s + 1);
int ans = 0;
for(int i = 1;i <= n;i++)
{
if(s[i] != 'B') continue;
int tmp = 0;
while(i - tmp - 1 >= 1 && i + tmp + 1 <= n && s[i - tmp - 1] == 'A' && s[i + tmp + 1] == 'C') ++tmp;
if(!tmp) continue;
int odd = (i - 1) & 1;
if(odd)
{
if(tmp >= 2) tmp--,ans++;
else
{
if(!rev) ans++;
else rev++,ans++;
tmp--;
}
}
if(tmp)
{
ans += min(tmp,rev + 1);
rev++;
}
}
cout<<ans;
return 0;
}
分类:
题解
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 如何调用 DeepSeek 的自然语言处理 API 接口并集成到在线客服系统
· 【译】Visual Studio 中新的强大生产力特性
· 2025年我用 Compose 写了一个 Todo App