Couriers 题解
我们知道,这题的观察的情况很特殊,可以从这个角度出发,窥探性质。
假设这个点是哞微子,通过模拟容易分析出第一次观察在原点往右一个单位,第二次观察在原点往左一个单位,接着原点往左两个单位,往右两个单位。
这个性质很好,然后我们很容易知道相邻两个肯定是不同的微子,因为两个不同微子相撞后相邻的还是不同的。
这样我们就可以用 \(O(1)\) 的时间复杂度来算出任意两个不同微子,相撞的时间。
这个算法也很简单,假设 a 为两微子中的哞微子,而 b 为反哞微子,则假设 $p_a <p_b $ 则 \(t = 2\times \lceil \frac{p_a-p_b}{s_a+s_b} \rceil-1\) ,反之则 \(t = 2\times \lceil \frac{p_a-p_b}{s_a+s_b} \rceil\) 。
那刚好这样的话我们可以想到用一个优先队列来维护 t ,将先发生的事件先处理,那此时我们需要一个能快速找到一个点的前驱和后继和单点删除的数据结构,是链表,也可以是 set ,反正已经是 \(O(n\log n)\) 随便用哪个不影响事件复杂度。
代码实现:
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=2E5+5;
int T;
int p[N],s[N],n,ans[N];
struct P {
int val,id1,id2;
bool operator<(const P &a)const {
return val>a.val ;
}
};
priority_queue<P>q;
set<int>st;
int solve(int p1,int p2,int s1,int s2,int op) {
if(op==-1)swap(p1,p2),swap(s1,s2);
if(p1<p2)return 2*((p2-p1-1)/(s1+s2))+1;
else return 2*((p1-p2-1)/(s1+s2))+2;
}
// 2*(p1-p2-1)/(s1+s2)+1
//
//分别表示第一个数的位置,第二个数的位置,两者的速度,第一个是正的还是负的
signed main() {
scanf("%lld",&T);
while(T--) {
while(!q.empty())q.pop();
st.empty();
scanf("%lld",&n);
for(int i=1; i<=n; i++)
scanf("%lld",p+i);
for(int i=1; i<=n; i++)
scanf("%lld",s+i);
for(int i=1; i<n; i++){
//cout<<solve(p[i],p[i+1],s[i],s[i+1],((i&1)?1:-1))<<endl;
q.push({solve(p[i],p[i+1],s[i],s[i+1],((i&1)?1:-1)),i,i+1});
st.insert(i);
}st.insert(n);
int cnt=0;
while(cnt<n/2){
int id1=q.top().id1,id2=q.top().id2,val=q.top().val;
q.pop();
if(st.find(id1) == st.end() || st.find(id2)==st.end())continue;
//cout<<id1<<" "<<id2<<endl;
cnt++;
ans[id1]=val,ans[id2]=val;
auto tmp1=st.lower_bound(id1);
if(tmp1!=st.begin()){
--tmp1;
auto tmp2=st.upper_bound(id2);
if(tmp2!=st.end())
q.push({solve(p[*tmp1],p[*tmp2],s[*tmp1],s[*tmp2],((*tmp1&1)?1:-1)),*tmp1,*tmp2});
}
st.erase(id1),st.erase(id2);
}
for(int i=1;i<=n;i++)
printf("%lld ",ans[i]);
puts("");
}
return 0;
}
//set

浙公网安备 33010602011771号