Description
炮艇大赛是一项危险的比赛。为了赢得这场比赛,参赛者可能会牺牲自己的生命。
参赛者将会在一条长度为 L 的环形轨道上比赛。在比赛开始时(0时刻),所有参赛者站在轨道不同的位置上,其中第 i 名参赛者站在位置 di ( ) 上。然后比赛开始。每位参赛者驾驶着它的炮艇,速度为 vi (速度可以为正,可以为负,也可以为0。速度为正表示参赛者顺时针移动,速度为负表示参赛者逆时针移动)。每位参赛者的速度都不同。
第 i 名参赛者有 i 点能量值。在比赛过程中,参赛者们可能会相遇(此处相遇指的是参赛者们在同一时刻恰好落在同一地点)。每两位参赛者 i,j 相遇时,能量值低的参赛者将被击毙出局。
当赛场上只剩下一个人时,比赛结束。
问比赛什么时候结束。
Input
第一行包含两个正整数 n,L ( )
接下来一行包含 n 个不同的整数 di ( )
接下来一行包含 n 个不同的整数 vi ( )
Output
输出一个分数 X/Y 表示结束时刻,其中 gcd(X,Y)=1 。若答案为0,应只输出“0”(不含引号)。
Solution
通过手推,可以发现只有相邻的两个参赛者才会最先相遇,所以这个问题变成了维护一个环相邻两元素的相遇时间。
我们可以用小根堆维护相邻两元素的相遇时间,为了便捷,我使用了优先队列。每次将相遇时间最短的一组相遇出队,看其中哪个能量值小,就把它标记为挂掉。此时会产生新的相邻,将其入队。第n-1次相遇的时间就是答案。
要注意,已经挂掉的人是不会与别人相遇的,所以相遇的两人之中若有人挂掉应忽略此次相遇。
Code
#include<bits/stdc++.h>
using namespace std;
struct data{
int t1,t2,x,y;
bool operator < (const data &v)const{
return double(t1)/double(t2)>double(v.t1)/double(v.t2);
}
}u;
struct xyj{
int d,v,id;
bool operator < (const xyj &y)const{
return d<y.d;
}
}a[100010];
int gcd(int x,int y){
return y==0?x:gcd(y,x%y);
}
priority_queue<data>q;
int n,nxt[100010],L;
int pre[100010];
bool out[100010];
void work(int x,int y){
int t1,t2;
if(a[x].v>a[y].v) swap(x,y);
t1=(a[x].d+L-a[y].d)%L;
t2=a[y].v-a[x].v;
q.push((data){t1,t2,x,y});
}
int main(){
scanf("%d%d",&n,&L);
for(int i=1;i<=n;i++){
scanf("%d",&a[i].d);
a[i].id=i;
}
for(int i=1;i<=n;i++)
scanf("%d",&a[i].v);
sort(a+1,a+n+1);
for(int i=1;i<=n;i++){
pre[i]=(i==1?n:i-1);
nxt[i]=(i==n?1:i+1);
}
for(int i=1;i<=n;i++){
int x=(i==n?1:i+1);
work(i,x);
}
int tot=0;
while(q.size()){
u=q.top(); q.pop();
if(out[u.x]||out[u.y]) continue;
tot++;
if(tot==n-1){//第n-1次相遇时间即是答案
int s=gcd(u.t1,u.t2);
printf("%d/%d",u.t1/s,u.t2/s);
return 0;
}
int lose=a[u.x].id>a[u.y].id?u.y:u.x;
out[lose]=true;
int x=pre[lose],y=nxt[lose];
nxt[x]=y,pre[y]=x,work(x,y);
}
}