• Codeforces 660C


    题目链接:http://codeforces.com/problemset/problem/660/C

    题意:

    给你一个长度为 $n$ 的 $01$ 串 $a$,记 $f(a)$ 表示其中最长的一段连续 $1$ 的长度。

    现在你最多可以将串中的 $k$ 个 $0$ 变成 $1$,求操作后的 $f(a)$。

    题解:

    (说实话这道题不看tag我不一定能想得出来……)

    首先考虑二分枚举答案,对于一个假定的 $f(a)=x$,我们需要判断能不能满足:

    用 $dp[i]$ 表示 $a[i-x+1], cdots, a[i]$ 的这一段上 $1$ 的数目,那么只要存在一个 $dp[i] + k ge x$ 就代表这个答案 $f(a)=x$ 是可行的。

    另外,考虑到二分的边界问题比较难处理,故在区间被缩得比较小的时候可以改用暴力枚举。

    同时,考虑到还有输出的问题,可以在让二分的check(mid)函数不是单纯返回 $0,1$,还可以再返回一个数代表答案子串的位置。

    AC代码:

    #include<bits/stdc++.h>
    using namespace std;
    typedef pair<int,int> P;
    #define mp(x,y) make_pair(x,y)
    #define fi first
    #define se second
    const int maxn=3e5+10;
    int n,k;
    int a[maxn];
    int f[maxn];
    P check(int x)
    {
        int mx=0, p=x;
        for(int i=1;i<=x;i++) f[i]=f[i-1]+a[i], mx=max(mx,f[i]);
    
        for(int i=x+1;i<=n;i++)
        {
            f[i]=f[i-1]-a[i-x]+a[i];
            if(f[i]>mx) mx=f[i], p=i;
        }
        return mp(mx+k>=x,p);
    }
    int main()
    {
        cin>>n>>k;
        for(int i=1;i<=n;i++) scanf("%d",&a[i]);
    
        int l=k, r=n, ans, pos;
        while(l<=r)
        {
            if(r-l<=5)
            {
                for(int mid=r;mid>=l;mid--)
                {
                    P res=check(mid);
                    if(!res.fi) continue;
                    ans=mid, pos=res.se;
                    break;
                }
                break;
            }
            int mid=(l+r)/2;
            if(check(mid).fi) l=mid;
            else r=mid;
        }
    
        cout<<ans<<endl;
        for(int i=1;i<=pos-ans;i++) printf("%d ",a[i]);
        for(int i=pos-ans+1;i<=pos;i++) printf("1 ");
        for(int i=pos+1;i<=n;i++) printf("%d ",a[i]);
    }
  • 相关阅读:
    java 问题记录
    java 构造方法
    java 接口
    java 抽象类
    java 封装
    java 面向对象
    java 集合小练习 超市库存管理系统
    linux常用指令
    个人简历表格
    html5 表格文档常用指令
  • 原文地址:https://www.cnblogs.com/dilthey/p/10473904.html
Copyright © 2020-2023  润新知