更好的阅读体验?点这里
题目:
幼儿园的 N N N ( 1 ≤ N ≤ 1 0 5 1 \le N \le 10^5 1≤N≤105)个小朋友沿着一条直线站成一排拍摄照片。第 i i i 个小朋友站的位置为 x [ i ] x[i] x[i]( 1 ≤ x [ i ] ≤ 1 0 9 1 \le x[i] \le 10^9 1≤x[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[]表示每个小朋友的位置和性别,显然要先按照位置从小到大排序。
因为三个条件放在一起不好做,所以可以分开处理:
-
连续区间中性别全为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) 。
-
连续区间中男女生个数相同
由于男生和女生个数相同,所以女生人数的相反数与男生个数的和为 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[i−1]=0 说明 i i i 到 j j j 这一段男生和女生个数相同。
假设 s [ j ] − s [ i − 1 ] = 0 s[j]-s[i-1]=0 s[j]−s[i−1]=0 ,则 s [ i − 1 ] = s [ j ] s[i-1]=s[j] s[i−1]=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[i−1]=0 可以推出 s [ j ] = s [ i − 1 ] s[j]=s[i-1] s[j]=s[i−1] ,所以记录答案时一定要写成"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