题目大意
在数轴上,初始有n个点,每个点有一个初始运动方向。
每个点的速度都是1/s
当两个点相遇时它们会改变自己的运动方向
q个询问每次询问求t秒后初始编号为k的点坐标是什么。
经典好题
结论1:相对位置永远不变。易证。
有了这个结论,我们相当于要求t秒后排名为rank[k]的点坐标。
结论2:可以不考虑变向。
因为现在与编号无关,两个点相遇转向可以理解为互相穿过。
结论3:初始方向相同的点相对位置永远不变,易证。
于是,我们把两个方向的点分别存下来并按坐标排序。
那么,同方向的点rank具有二分性!
我们可以在其中一个方向的点里二分,如何求一个点t秒后的排名?只需要看其和另一个方向的点碰到了多少次以及其初始排名,那么再另一个方向的点里二分找出碰了多少次。
如果这个方向里的点没有排名为k的,就在另一个方向的点里做类似过程。
可能有某时刻一个坐标多个点,但易知这种情况这个坐标上只会有两个方向不同的点,因此可以区分rank。
注意处理所有点方向都相同的情况。
#include<cstdio>
#include<algorithm>
#include<map>
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
const int maxn=200000+10;
int p[maxn],a[maxn],b[maxn],c[maxn],rank[maxn];
bool bz[maxn];
int i,j,k,l,r,mid,t,n,m,top,tot,ans;
bool czy;
int read(){
int x=0,f=1;
char ch=getchar();
while (ch<'0'||ch>'9'){
if (ch=='-') f=-1;
ch=getchar();
}
while (ch>='0'&&ch<='9'){
x=x*10+ch-'0';
ch=getchar();
}
return x*f;
}
bool cmp1(int a,int b){
return p[a]<p[b];
}
int getrank1(int x,int y){
int l,r,mid,k;
l=1;r=tot;
while (l<r){
mid=(l+r)/2;
if (p[b[mid]]<p[x]) l=mid+1;else r=mid;
}
k=l;
if (p[b[k]]<p[x]) return rank[x];
if (p[b[k]]-y>=p[x]+y) return rank[x];
l=k;r=tot;
while (l<r){
mid=(l+r+1)/2;
if (p[b[mid]]-y<p[x]+y) l=mid;else r=mid-1;
}
return rank[x]+l-k+1;
}
int getrank2(int x,int y){
int l,r,mid,k;
l=1;r=top;
while (l<r){
mid=(l+r+1)/2;
if (p[a[mid]]>p[x]) r=mid-1;else l=mid;
}
k=l;
if (p[a[k]]>p[x]) return rank[x];
if (p[a[k]]+y<=p[x]-y) return rank[x];
l=1;r=k;
while (l<r){
mid=(l+r)/2;
if (p[a[mid]]+y>p[x]-y) r=mid;else l=mid+1;
}
return rank[x]-(k-l+1);
}
int main(){
freopen("bridge.in","r",stdin);freopen("bridge.out","w",stdout);
n=read();
fo(i,1,n) p[i]=read();
fo(i,1,n) c[i]=i;
sort(c+1,c+n+1,cmp1);
fo(i,1,n) rank[c[i]]=i;
fo(i,1,n) bz[i]=read();
fo(i,1,n)
if (bz[i]) a[++top]=i;else b[++tot]=i;
sort(a+1,a+top+1,cmp1);
sort(b+1,b+tot+1,cmp1);
m=read();
while (m--){
k=read();t=read();
k++;
if (!top||!tot){
if (bz[k]) printf("%d\n",p[k]+t);else printf("%d\n",p[k]-t);
continue;
}
k=rank[k];
l=1;r=top;
while (l<r){
mid=(l+r)/2;
if (getrank1(a[mid],t)<k) l=mid+1;else r=mid;
}
if (getrank1(a[l],t)==k) ans=a[l];
else{
l=1;r=tot;
while (l<r){
mid=(l+r)/2;
if (getrank2(b[mid],t)<k) l=mid+1;else r=mid;
}
ans=b[l];
}
if (bz[ans]) printf("%d\n",p[ans]+t);else printf("%d\n",p[ans]-t);
}
}