魔法石(二分+前缀和)

时间限制: 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
****************************************************************/

猜你喜欢

转载自blog.csdn.net/upc122/article/details/106064708
今日推荐