P15847 [NOISG 2026 Finals] 猴子 / Monkeys 题解
水篇题解。
感觉已经好久好久没写过黄题题解了吧。
日常看错题之当我以为一个猴子在点
上一次看错题,被某个巨佬扯出来搞到一个紫题,吓哭了。
感觉是比较一眼的题目。当然这建立在你题看对了的情况下。
一个点只会朝着一个方向永远移动,所以相遇只有两种情况:
-
相向而行的相遇:
- 这要求两点中,最初位于左侧的点是往右走,最初位于右侧的点是往左走。
- 且两点之间的间隔
y-x 为偶数(奇数的话会擦边而过,但无法在整点上相遇),还要求y-x \le 2k (否则在k 时间内也无法相遇)。
-
同向而行的相遇:
- 正常来讲,同向而行能相遇是因为一个快一个慢。
- 但是这道题中,所有点的移动速度都是
1 ,这也就不存在“追及”情况了,也就是说,两个同向而行的点能相遇,当且仅当其初始点位置相同。也就是两个本质一样的点。
那么就可以做了,维护三个数组
对这三个序列升序排序之后,就很好做了。
相向而行的,枚举
同向而行的,只需要分往左还是往右,对下标开两个桶记录一下,贡献
整个还是非常好做的。
代码里有一些辅助理解的注释喵。
::::success[code && submission]
#include<bits/stdc++.h>
#define LL long long
#define UInt unsigned int
#define ULL unsigned long long
#define LD long double
#define pii pair<int,int>
#define pLL pair<LL,LL>
#define pDD pair<LD,LD>
#define fr first
#define se second
#define pb push_back
#define isr insert
#define _i128 __int128
using namespace std;
const int N = 2e5+5;
LL n,k,p[N],Ans;//不开 LL 见祖宗
vector<LL> L[2],R;//其实应该开数组的但是实在不想写三个长度变量
map<LL,LL> t[2];//充当桶解决重复点的问题
int read(){
int su=0,pp=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')pp=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){su=su*10+ch-'0';ch=getchar();}
return su*pp;
}
int main(){
n=read(),k=read();
for(int i=1;i<=n;i++)p[i]=read();
//注意 vector 的下标从 0 开始!!
for(int i=1;i<=n;i++){
char opt;cin>>opt;
if(opt=='R')R.pb(p[i]);
else L[p[i]&1].pb(p[i]);
//按点位的奇偶性分类
//因为中间间隔路程只能是偶数(两只猴子都在移动)
t[(opt=='L'?0:1)][p[i]]++;//塞进桶
}
sort(R.begin(),R.end());
sort(L[0].begin(),L[0].end());
sort(L[1].begin(),L[1].end());
for(LL x:R){
//以往右走的为指定物,找右侧奇偶性匹配且距离不长的猴子
int o=(x&1);//奇偶性判定
int l=lower_bound(L[o].begin(),L[o].end(),x)-L[o].begin();
int r=upper_bound(L[o].begin(),L[o].end(),x+2*k)-L[o].begin()-1;
//找到能相遇的猴子的左右端点
//注意右端点要用 upper_bound 最后 -1 因为可能有重复!
Ans+=r-l+1;//累加答案
}
for(int o=0;o<2;o++)//左右方向
for(auto [x,y]:t[o])Ans+=y*(y-1)/2;//累加答案
cout<<Ans<<"\n";
return 0;
}
::::
如果本篇题解对你有帮助的话,麻烦你点一个小小的赞,真是太感谢啦!