错题重错之WYT的刷子 单调队列

题目描述

WYT有一把巨大的刷子,刷子的宽度为M米,现在WYT要使用这把大刷子去粉刷有N列的栅栏(每列宽度都为1米;每列的高度单位也为米,由输入数据给出)。

使用刷子的规则是:

与地面垂直,从栅栏的底部向上刷
每次刷的宽度为M米(当剩余栅栏宽度不够M米的话,刷子也可以使用,具体看样例2)
对于连续的M列栅栏,刷子从底向上,刷到的高度只能到这M列栅栏的最低高度。
WYT请你回答两个问题:

最少有多少个单位面积不能刷到(单位面积为1平米)
在满足第一问的条件下,最少刷几次?

输入格式

共两行:

第一行两个整数N和M。

第二行共N个整数,表示N列栅栏的高度

输出格式

一行,两个整数,分别为最少剩余的单位面积数量和最少刷的次数。

样例

样例输入1

5 3
5 3 4 4 5

样例输出1

3
2

样例输入2

10 3
3 3 3 3 3 3 3 3 3 3

样例输出2

0
4

样例输入3

7 4
1 2 3 4 3 2 1

样例输出3

4
4

样例1的解释:

高度分别为 5 3 4 4 5 如上:

黄色的方块表示共有3个单位面积没刷上

绿色的框和红色的框表示一共刷了两次。

数据范围与提示

30%的数据:N<=10^3

50%的数据:N<=10^5

100%的数据:1<=N<=10^6, 1<=M<=10^6,N>=M, 每列栅栏的高度<=10^6.

分析

如果某一个栅栏能被刷完,他向左和向右延伸的宽度(包括自己)之和应该不小于刷子的宽度 M、
因此,我们可以先处理一下每个栅栏向左向右延伸的宽度。
得到了延伸的宽度之后,我们也就可以判断每个栅栏是否能被完整的粉刷
可以先给每个栅栏标记一下,即如果延伸的宽度不小于 M,则标记为能刷完,否则标记为刷不完
因为要求最少剩下的方块数量,下一步我们就可以求一下每个栅栏还剩多少不能刷,递推处理一下每个栅栏能被刷的最大高度。
我们先从左往右考虑一下,如果第i个栅栏能被刷完,那么最大高度就是它本身的高度;
如果不能,则它的高度一定比它左边的栅栏能刷的最大高度要大(如果不成立,那么它完全可以接在它左边的栅栏上,把自己刷完)
因此它能刷到的最大高度就是它左边的栅栏能刷到的最大高度。
然后还要从右边处理一下,有可能对于每一个栅栏,右边过来可能刷的更高,所以需要求一下最值。
既然有了每个栅栏能刷的最大高度,那么用每个栅栏的高度之和减去所有能刷的最大高度就是剩余的面积。
要求刷的次数,因为每个栅栏都要刷到最高,所以贪心刷就可以了。
如果几个连续的栅栏刷的最大高度相同,那么就一起刷,宽度除以 M 就是次数。如果相邻两个刷的高度不一样,那么从此处断开重新刷就可以了。

代码

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e6+5;
int gd[maxn],l[maxn],r[maxn],que[maxn],vis[maxn],sd[maxn];
int main(){
    int n,m;
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++){
        scanf("%d",&gd[i]);
    }
    int head=0,tail=-1;
    for(int i=1;i<=n+1;i++){
        while(head<=tail && gd[i]<gd[que[tail]]){
            r[que[tail]]=i-que[tail];
            tail--;
        }
        que[++tail]=i;
    }
    memset(que,0,sizeof(que));
    head=0,tail=-1;
    for(int i=n;i>=0;i--){
        while(head<=tail && gd[i]<gd[que[tail]]){
            l[que[tail]]=que[tail]-i;
            tail--;
        }
        que[++tail]=i;
    }
    for(int i=1;i<=n;i++){
        if(l[i]+r[i]-1>=m) vis[i]=1;
    }
    for(int i=1;i<=n;i++){
        if(vis[i]) sd[i]=gd[i];
        else sd[i]=sd[i-1];
    }
    for(int i=n;i>=1;i--){
        if(!vis[i]) sd[i]=max(sd[i],sd[i+1]);
    }
    long long sum=0;
    for(int i=1;i<=n;i++){
        sum+=(long long)(gd[i]-sd[i]);
    }
    printf("%lld\n",sum);
    int now=2,cs=0;
    while(now<=n+1){
        int cnt=1;
        while(now<=n+1 && sd[now]==sd[now-1]){
            cnt++;
            now++;
        }
        if(cnt%m==0) cs+=cnt/m;
        else cs+=(cnt/m+1);
        now++;
    }
    printf("%d\n",cs);
    return 0;
}
posted @ 2020-07-11 20:09  liuchanglc  阅读(176)  评论(0编辑  收藏  举报