时间限制: 1 Sec 内存限制: 128 MB
[提交] [状态]
题目描述
你习得了魔法,并学会了熟练运用魔法石。
你得到了n颗魔法石,魔法石有两种属性,分别为火属性和水属性。你一开始得到的是这n颗魔法石的一个排列。定义这n颗魔法石释放出来的能量,为最长的属性相同的魔法石连续段的长度。
作为一名熟练的魔法师,你还可以至多修改k个魔法石的属性。你现在想知道这n颗魔法石最多可以释放出多少能量。
输入
第一行为两个正整数n,k,表示魔法石的个数和最多可以修改的魔法石数量。
接下来一行为一个长度为n的字符串,第i个字符表示第i颗魔法石的属性,a为火属性,b为水属性。
输出
输出为一行一个正整数,表示这n颗魔法石最多可以释放出的能量大小。
样例输入 Copy
【样例1】
4 2
abba
【样例2】
8 1
aabaabaa
样例输出 Copy
【样例1】
4
【样例2】
5
提示
对于30%的数据,满足n≤20。
对于60%的数据,满足n≤1000。
对于另外20%的数据,满足所有魔法石属性均相同。
对于100%的数据,满足1≤k≤n≤10^5。
考查前缀和+二分答案。
首先分别统计a,b的个数,如果可修改的数量不小于a,b的最小值,那么一定可以修改成所有元素均为a或均为b。对于可修改的数量小于a,b的最小值的情况,可以通过二分答案来解决。
接下来的主要思路是利用前缀和记录前i个元素中含有a或b的个数,可以这样考虑:固定住左区间,向右移动右区间端点值,随着右端点值的右移,含有a(或b)的个数越来越多,因此一定存在一个位置使得a(或b)的个数恰好等于要修改的数量。遍历整个区间,对于每一个左端点i,采用二分法确定[i,n]内可以修改的元素个数。
注意不能根据a和b的大小关系确定修改的元素,因为若干次修改的元素不一定是同一个元素,而且如何修改要看元素间的相邻情况。要解决这个问题,可以先统计修改a得到的最大值,然后再统计修改b得到的最大值,两者取最大即可。
#include<cstdio>
#include<algorithm>
using namespace std;
char stone[100005];
int s[100005]={
0};//用于修改b,将b对应的位置记为1
int t[100005]={
0};//用于修改a,将a对应的位置记为1
int main()
{
int n,k,i,a=0,b=0,l,r,mid,ans=0,t1;
scanf("%d %d",&n,&k);
scanf("%s",stone+1);
for(i=1;i<=n;i++)
{
if(stone[i]=='a')
{
a++;
s[i]=0;
t[i]=1;
}
else if(stone[i]=='b')
{
b++;
s[i]=1;
t[i]=0;
}
}
if(k<min(a,b))
{
/*第一部分用于确定修改b得到的最大值*/
for(i=1;i<=n;i++)s[i]+=s[i-1];//前缀和,为1表示该位置的元素要修改
for(i=1;i<=n;i++)
{
l=i;
r=n;
t1=0;
while(l<=r)//二分答案
{
mid=(l+r)/2;
if(s[mid]-s[i-1]<=k)
{
t1+=mid-l+1;
l=mid+1;
}
else r=mid-1;
}
ans=max(ans,t1);
}/*第二部分用于确定修改a得到的最大值*/
for(i=1;i<=n;i++)t[i]+=t[i-1];//前缀和,为1表示该位置的元素要修改
for(i=1;i<=n;i++)
{
l=i;
r=n;
t1=0;
while(l<=r)//二分答案
{
mid=(l+r)/2;
if(t[mid]-t[i-1]<=k)
{
t1+=mid-l+1;
l=mid+1;
}
else r=mid-1;
}
ans=max(ans,t1);
}
printf("%d",ans);
}
else printf("%d",n);
return 0;
}
/**************************************************************
Language: C++
Result: 正确
Time:28 ms
Memory:2000 kb
****************************************************************/