独木桥

题目大意

在数轴上,初始有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);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值