bzoj5191 [Usaco2018 Feb]Slingshot

http://www.elijahqi.win/2018/03/05/bzoj5191/
Description

FarmerJohn最讨厌的农活是运输牛粪。为了精简这个过程,他产生了一个新奇的想法:与其使用拖拉机拖着装满牛
粪的大车从一个地点到另一个地点,为什么不用一个巨大的便便弹弓把牛粪直接发射过去呢?(事实上,好像哪里
不太对……)FarmerJohn的农场沿着一条长直道路而建,所以他农场上的每个地点都可以简单地用该地点在道路上
的位置来表示(相当于数轴上的一个点)。FJ建造了N个弹弓(1≤N≤10^5),其中第i个弹弓可以用三个整数xi,
yi以及ti描述,表示这个弹弓可以在ti单位时间内将牛粪从位置xi发射到位置yi。FJ有M堆牛粪需要运输(1≤M≤1
0^5)。第j堆牛粪需要从位置aj移动到位置bj。使用拖拉机运输牛粪,经过路程d需要消耗d单位时间。FJ希望通过
对每一堆牛粪使用至多一次弹弓来减少运输时间。FJ驾驶没有装载牛粪的拖拉机的时间不计。对这M堆牛粪的每一
堆,在FJ可以在运输过程中使用至多一次弹弓的条件下,帮助FJ求出其最小运输时间。

Input

输入的第一行包含N和M。
下面N行,每行用xi,yi,ti(0≤xi,yi,ti≤10^9)描述了一个弹弓。
最后M行用aj和bj描述了需要移动的牛粪。

Output

输出M行,每堆牛粪一行,表示运输这堆牛粪需要的最短时间。

Sample Input

2 3
0 10 1
13 8 2
1 12
5 2
20 7
Sample Output

4
3
10
在这里,第一堆牛粪需要从位置1移动到位置12。不使用弹弓的话,会消耗11单位时间。但是,如果使用第一个弹
弓,只需消耗1单位时间移动到位置0(弹弓的发射点),1单位时间将弹弓通过空中弹射并着陆在位置10(弹弓的
目的地),然后2单位时间把牛粪移动到位置12。第二堆牛粪的最佳移动方案是不使用弹弓,第三堆牛粪应该使用
第二个弹弓。

考虑所有情况都是|a-x|+|b-y|+t 那么我们分情况讨论
比如a < x b < y 打开绝对值变成-a-b+x+y+t 那么我针对后面的x+y+t维护一下 因为前面每个询问的-a-b都是唯一确定的所以相当于我只需要后面最小即可 考虑到所有情况都是前缀的二维数点 所以我们可以离线之后树状数组维护这个最小值 分别分类讨论四次即可

#include<cmath>
#include<cstdio>
#include<algorithm>
#define ll long long
#define N 110000
#define inf 1LL<<60
using namespace std;
ll c[N<<2];
inline char gc(){
    static char now[1<<16],*S,*T;
    if (T==S){T=(S=now)+fread(now,1,1<<16,stdin);if(T==S) return EOF;}
    return *S++;
}
inline int read(){
    int x=0,f=1;char ch=gc();
    while(ch<'0'||ch>'9') {if (ch=='-') f=-1;ch=gc();}
    while(ch<='9'&&ch>='0') x=x*10+ch-'0',ch=gc();
    return x*f;
}
inline int abs(int x){
    return x<0?-x:x;
}
struct node{
    int a,b,id,bb;
}qr[N];
struct node1{
    int x,y,t,yy;
}sl[N];
ll ans[N],s[N<<1];
int aa[N<<1],nn,n,m;
inline void add(int x,ll v){for (;x<=nn;x+=(x&-x)) s[x]=min(s[x],v);}
inline ll query(int x){ll tmp=inf;for (;x;x-=x&(-x)) tmp=min(tmp,s[x]);return tmp;}
inline bool cmp1(const node &a,const node &b){return a.a<b.a;}
inline bool cmp2(const node1 &a,const node1 &b){return a.x<b.x;}
int main(){
    freopen("slingshot.in","r",stdin);
    freopen("slingshot.out","w",stdout);
    n=read();m=read();int now=1;
    for (int i=1;i<=n;++i) sl[i].x=read(),sl[i].y=aa[++nn]=read(),sl[i].t=read();
    for (int i=1;i<=m;++i) qr[i].a=read(),qr[i].b=aa[++nn]=read(),qr[i].id=i,ans[i]=abs(qr[i].a-qr[i].b);
    sort(aa+1,aa+nn+1);nn=unique(aa+1,aa+nn+1)-aa-1;sort(sl+1,sl+n+1,cmp2);sort(qr+1,qr+m+1,cmp1);
    for (int i=1;i<=n;++i) sl[i].yy=lower_bound(aa+1,aa+nn+1,sl[i].y)-aa;
    for (int i=1;i<=m;++i) qr[i].bb=lower_bound(aa+1,aa+nn+1,qr[i].b)-aa;
    for (int i=1;i<=nn;++i) s[i]=inf;
    for (int i=1;i<=m;++i){
        while(now<=n&&sl[now].x<=qr[i].a) add(sl[now].yy,(ll)-sl[now].x-sl[now].y+sl[now].t),++now;
        ans[qr[i].id]=min(ans[qr[i].id],(ll)qr[i].a+qr[i].b+query(qr[i].bb));
    }for (int i=1;i<=nn;++i) s[i]=inf;now=1;
    for (int i=1;i<=m;++i){
        while(now<=n&&sl[now].x<=qr[i].a) add(nn-sl[now].yy+1,(ll)-sl[now].x+sl[now].y+sl[now].t),++now;
        ans[qr[i].id]=min(ans[qr[i].id],(ll)qr[i].a-qr[i].b+query(nn-qr[i].bb+1));
    }for (int i=1;i<=nn;++i) s[i]=inf;now=n;
    for (int i=m;i>=1;--i){
        while(now>=1&&sl[now].x>=qr[i].a) add(sl[now].yy,(ll)sl[now].x-sl[now].y+sl[now].t),--now;
        ans[qr[i].id]=min(ans[qr[i].id],(ll)-qr[i].a+qr[i].b+query(qr[i].bb));
    }for (int i=1;i<=nn;++i) s[i]=inf;now=n;
    for (int i=m;i>=1;--i){
        while(now>=1&&sl[now].x>=qr[i].a) add(nn-sl[now].yy+1,(ll)sl[now].x+sl[now].y+sl[now].t),--now;
        ans[qr[i].id]=min(ans[qr[i].id],(ll)-qr[i].a+-qr[i].b+query(nn-qr[i].bb+1));
    }
    for (int i=1;i<=m;++i) printf("%lld\n",ans[i]);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值