[IOI2019] 天桥

这是一道很好的由部分分推正解的题目。

首先前两档部分分就直接暴力找到所有的交点,然后跑dijkstra就行。这个启示我们要尝试减少交点的个数达到O(n)级别,然后连边跑dijkstra。(大致的思路是,找点->连边->跑最短路)

先考虑三四档的部分分,起点和重点一定在两边,我们只需要上下行走或者向右行走。
根据贪心的策略,高处的天桥一定会优于低处的天桥,因此我们可以对行走方式增加两条法则:1.不提前下天桥(要么上到别的天桥,要么就一直走到头);2.立刻上天桥(如果某个天桥要上去,那么一定在左端点上)。于是我们只需要保留每个天桥的两端,然后是每个点在其下方第一条天桥上的投影。

现在考虑正解,由于对称性,这里我们只讨论起点不在边界的其情况,这种情况会多增加三种类型的天桥:全部在在起点左边,包括起点,但起点的建筑够不到/够得到。如果全部在起点左边,我们也只考虑端点,道理和上面一样;如果够得到,我们就取端点加上与起点建筑相交的点;如果够不到,我们依然要尝试尽可能早的上桥,因此只用取起点建筑左边第一能上桥的建筑和右边第一个能上桥的建筑。

总的来说,这道题的思路不算很难,但是代码很难打,每一个板块的细节都很多,调试了很久才过。

#include<bits/stdc++.h>
#define rep(i,x,y) for(int i=x;i<=y;i++)
#define dwn(i,x,y) for(int i=x;i>=y;i--)
#define pii pair<int,int>
#define ll long long
using namespace std;
template<typename T>inline void qr(T &x){
  x=0;char s=getchar();
  while(!isdigit(s))s=getchar();
  while(isdigit(s))x=x*10+s-48,s=getchar();
}
const int N=1e5+10,M=1e6+10;
int n,m;
struct node1{int x,h;}a[N];
struct node2{int l,r,h;}b[N];

int S,T;
map<pii,int>num;int cnt;pii c[M];
int st[N][20],lg[N];

int tp,sta[N];
vector<int>bd[N],br1[N],br2[N],h2[N];
vector<pii>h1[N];

int tot,hd[M],ver[M<<2],nxt[M<<2],w[M<<2];
ll d[M];bool v[M];

void add(int x,int y,int ww){
  tot++;
  ver[tot]=y,w[tot]=ww;
  nxt[tot]=hd[x];
  hd[x]=tot;
}
void add_point(int x,int y){
  pii tt=make_pair(x,y);
  if(num.find(tt)==num.end()){
    num[tt]=++cnt;
    c[cnt]=tt;
  }
}
bool pd1(int l,int r,int x){
  return l<x&&x<r;
}
int query(int l,int r){
  int t=lg[r-l+1];
  return max(st[l][t],st[r-(1<<t)+1][t]);
}
int pre(int x,int h){
  int l=1,r=x,mid,pos;
  while(l<=r){
    mid=(l+r)/2;
    if(query(mid,x)>=h)pos=mid,l=mid+1;
    else r=mid-1;
  }
  return pos;
}
int nxtt(int x,int h){
  int l=x,r=n,mid,pos;
  while(l<=r){
    mid=(l+r)/2;
    if(query(x,mid)>=h)pos=mid,r=mid-1;
    else l=mid+1;
  }
  return pos;
}
int main(){
  qr(n),qr(m);
  rep(i,1,n)qr(a[i].x),qr(a[i].h);
  rep(i,1,m){
    qr(b[i].l),qr(b[i].r),qr(b[i].h),b[i].l++,b[i].r++;
  }
  qr(S),qr(T),S++,T++;
  
  lg[0]=-1;
  rep(i,1,n){
    st[i][0]=a[i].h;
    lg[i]=lg[i>>1]+1;
  }
  rep(i,1,lg[n]){
    rep(j,1,n-(1<<i)+1){
      st[j][i]=max(st[j][i-1],st[j+(1<<(i-1))][i-1]);
    }
  }
  
  add_point(S,0);
  add_point(T,0);
  rep(i,1,m){
    if(!pd1(b[i].l,b[i].r,S)&&!pd1(b[i].l,b[i].r,T)){
      add_point(b[i].l,b[i].h);
      add_point(b[i].r,b[i].h);
    }
    else{
      if(pd1(b[i].l,b[i].r,S)){
        if(b[i].h>a[S].h){
          add_point(pre(S,b[i].h),b[i].h);
          add_point(nxtt(S,b[i].h),b[i].h);
        }
        else add_point(b[i].l,b[i].h),add_point(S,b[i].h);
      }
      else add_point(b[i].l,b[i].h);
      if(pd1(b[i].l,b[i].r,T)){
        if(b[i].h>a[T].h){
          add_point(pre(T,b[i].h),b[i].h);
          add_point(nxtt(T,b[i].h),b[i].h);
        }
        else add_point(b[i].r,b[i].h),add_point(T,b[i].h);
      }
      else add_point(b[i].r,b[i].h);
    }
  }
  
  rep(i,1,cnt)bd[c[i].first].push_back(c[i].second);
  rep(i,1,m)br1[b[i].l].push_back(b[i].h),br2[b[i].r].push_back(b[i].h);
  set<int>s;
  set<int>::iterator it;
  rep(i,1,n){
    for(int h:br2[i])s.erase(s.find(h));
    for(int h:br1[i]){
      s.insert(h);
    }
    for(int h:bd[i]){
      it=s.lower_bound(h);
      if(it!=s.begin()){
        it--;
        add_point(i,*it);
      }
    }
  }
  rep(i,1,n)bd[i].clear();
  rep(i,1,cnt)bd[c[i].first].push_back(c[i].second);
  rep(i,1,n){
    sort(bd[i].begin(),bd[i].end());
    int last=-1;
    for(int h:bd[i]){
      if(h>a[i].h)break;
      int now=num[make_pair(i,h)];
      if(last!=-1){
        add(last,now,h-c[last].second);
        add(now,last,h-c[last].second);
      }
      last=now;
    }
  }
  rep(i,1,m)sta[++tp]=b[i].h;
  sort(sta+1,sta+tp+1);
  tp=unique(sta+1,sta+tp+1)-sta-1;
  rep(i,1,m){
    b[i].h=lower_bound(sta+1,sta+tp+1,b[i].h)-sta;
    h1[b[i].h].push_back(make_pair(b[i].l,b[i].r));
  }
  rep(i,3,cnt){
    c[i].second=lower_bound(sta+1,sta+tp+1,c[i].second)-sta;
    h2[c[i].second].push_back(c[i].first);
  }
  rep(i,1,tp){
    sort(h1[i].begin(),h1[i].end());
    sort(h2[i].begin(),h2[i].end());
    int last=-1,now,j=0;
    for(int x:h2[i]){
      now=num[make_pair(x,sta[i])];
      if(last!=-1&&h1[i][j].first<=c[last].first&&x<=h1[i][j].second){
        add(last,now,a[x].x-a[c[last].first].x);
        add(now,last,a[x].x-a[c[last].first].x);
      }
      if(x==h1[i][j].second)j++;
      last=now;
    }
  }
  S=1,T=2;
  priority_queue<pair<ll,int> >q;
  rep(i,1,cnt)d[i]=(ll)1e18;
  d[S]=0;q.push(make_pair(-d[S],S));
  while(q.size()){
    int x=q.top().second;q.pop();
    if(v[x])continue;
    v[x]=1;
    for(int i=hd[x];i;i=nxt[i]){
      int y=ver[i];
      if(d[y]>d[x]+(ll)w[i]){
        d[y]=d[x]+(ll)w[i];
        q.push(make_pair(-d[y],y));
      }
    }
  }
  if(d[T]==(ll)1e18)d[T]=-1;
  cout<<d[T]<<endl;
  return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值