[桶+前缀和]拍摄照片

9 篇文章 0 订阅

更好的阅读体验?点这里

题目:

幼儿园的 N N N 1 ≤ N ≤ 1 0 5 1 \le N \le 10^5 1N105)个小朋友沿着一条直线站成一排拍摄照片。第 i i i 个小朋友站的位置为 x [ i ] x[i] x[i] 1 ≤ x [ i ] ≤ 1 0 9 1 \le x[i] \le 10^9 1x[i]109),性别为 s [ i ] s[i] s[i] s [ i ] s[i] s[i] 为"B"或"G"),没有两个小朋友站在相同的位置上。

现在老师想要拍摄一张照片,显然照片是包含小朋友的一个连续区间。为了使照片和谐,老师要求拍摄的照片中要么全是男孩(B),要么全是女孩(G),要么男孩(B)和女孩(G)的人数相同。

老师希望你能帮他拍摄一张最大长度的照片,照片的长度是指:照片中最右边小朋友的位置与照片中最左边小朋友的位置差,如果老师拍摄到的照片只有一个人,那么照片长度就为 0 0 0

分析:

用结构体数组 a [ ] a[] a[]表示每个小朋友的位置和性别,显然要先按照位置从小到大排序。

因为三个条件放在一起不好做,所以可以分开处理:

  1. 连续区间中性别全为B或G

    用两个指针 i i i j j j i i i 1 1 1 N N N ,每次让 j j j i + 1 i+1 i+1 处开始找,如果 a [ i ] . s = = a [ j ] . s a[i].s==a[j].s a[i].s==a[j].s ,那么 j + 1 j+1 j+1 ,否则记录答案。最后让 i = j i=j i=j ,开始下一轮循环。

    因为 i i i j j j 都不往回走,所以时间复杂度为 O ( n ) O(n) O(n)

  2. 连续区间中男女生个数相同

    由于男生和女生个数相同,所以女生人数的相反数与男生个数的和为 0 0 0(反之亦然)。因此可以将女生性别记为 − 1 -1 1 ,男生性别记为 1 1 1 。这样记性别的前缀和为 s [ ] s[] s[] ,如果 s [ j ] − s [ i − 1 ] = 0 s[j]-s[i-1]=0 s[j]s[i1]=0 说明 i i i j j j 这一段男生和女生个数相同。

    假设 s [ j ] − s [ i − 1 ] = 0 s[j]-s[i-1]=0 s[j]s[i1]=0 ,则 s [ i − 1 ] = s [ j ] s[i-1]=s[j] s[i1]=s[j] 。意思就是如果计算到第 j j j 个位置时,前面出现过 s [ j ] s[j] s[j] ,说明 i i i j j j 这一段男生个数与女生个数相同。那怎么快速知道 i i i 的位置呢?

    p o s [ s ] pos[s] pos[s] 表示前缀和为 s s s 时第一次出现的位置。 这样可以从 1 1 1 n n n 扫描,每次记 s s s 为前缀和,如果 s s s 出现过,那么就记录答案,否则 p o s [ s ] = i pos[s]=i pos[s]=i

    注意:

    • 因为由 s [ j ] − s [ i − 1 ] = 0 s[j]-s[i-1]=0 s[j]s[i1]=0 可以推出 s [ j ] = s [ i − 1 ] s[j]=s[i-1] s[j]=s[i1] ,所以记录答案时一定要写成"a[i].x-a[pos[i]+1].x" ,原因是 p o s [ s ] = i pos[s]=i pos[s]=i 时这个 i i i 代表的是“i-1”,因此计算时要加上 1 1 1

    • s s s 有可能为负数,所以要对 p o s [ ] pos[] pos[] 处理一下,每次记录时加上一个常量。

    最后在两种方式中选最大值就行了,具体细节见代码:

代码:

#include <cstdio>
#include <algorithm>
#define N 100000//常量不要太大,因为s最小是-100000,所以N=10^5就够了
struct kid{
    int s,x;
}a[N+3];//数组多开一点,下同
bool cmp(kid a,kid b){//排序用的
    return a.x<b.x;
}
int n,ans=-(1<<30),s,pos[(N<<1)+3];
//变量含义见上文,注意pos一定要多开一倍,因为每次都将下标加了一个常量
int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        char c[2];
        scanf("%d%s",&a[i].x,c);//用这样的方法可以防止读入空格、换行符等
        a[i].s=c[0]=='B'?1:-1;
    }
    std::sort(a+1,a+1+n,cmp);//排序
    int i=1,j;
    while(i<=n){//第一种情况
        for(j=i+1;a[i].s==a[j].s;j++);
        ans=std::max(ans,a[j-1].x-a[i].x);//注意要写j-1,因为第j个已经不符合条件了
        i=j;
    }
    for(i=1;i<=n;i++){//第二种情况
        s+=a[i].s;
        if(pos[N+s]) ans=std::max(ans,a[i].x-a[pos[N+s]+1].x);//就用这种方式处理
        else pos[N+s]=i;//如果写成pos[N+s]=i+1则上面一句不用+1
    }
    printf("%d\n",ans);
    return 0;
}

测试时没写出来QAQ

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值