HDU5669 Road 分层最短路+线段树建图

分析:(官方题解)

首先考虑暴力,显然可以直接每次O(n^2)

​的连边,最后跑一次分层图最短路就行了.

然后我们考虑优化一下这个连边的过程 ,因为都是区间上的操作,所以能够很明显的想到利用线段树来维护整个图,

连边时候找到对应区间,把线段树的节点之间连边.这样可以大大缩减边的规模,然后再跑分层图最短路就可以了.

但是这样建图,每一次加边都要在O(logn)个线段树节点上加边,虽然跑的非常快,但是复杂度仍然是不科学的.

为了解决边的规模的问题,开两棵线段树,连边时候可以新建一个中间节点,在对应区间的节点和中间节点之间连边

进一步缩减了边的规模,强行下降一个数量级

最后跑一下分层图最短路就行了

复杂度O(mlog^2n)

什么你会线段树但是不会分层图最短路?安利JLOI2011飞行路线.

因为边的数目还是相对比较多的,所以不能使用SPFA,而要使用Heap-Dijkstra来做最短路,

但是不排除某些厉害的选手有特殊的SPFA姿势可以做或者普通 SPFA写的比较优美就不小心跑过去了...

注:出题人的题解写的很详细了,然后JLOI2011飞行路线是BZOJ2763 直接去做就好了

      然后我的建图刚开始不太完善,跑了600+ms,然后后来完善了一下,按线段树节点建图(这就是题解)

     不过每个线段树的节点不需要和它的区域内所有的点连边,只需要按照线段树的结构,连它的左右儿子就行了

代码:

#include <stdio.h>
#include <iostream>
#include <algorithm>
#include <vector>
#include <queue>
#include <string.h>
using namespace std;
typedef long long LL;
const int INF=0x3f3f3f3f;
const int N=5e4+1;
int d[11][N*10+20],head[N*10+20],tot,n,m,k;
struct Edge{
  int v,w,next;
};
vector<Edge>edge;
void add(int u,int v,int w){
  edge.push_back(Edge{v,w,head[u]});
  head[u]=edge.size()-1;
}
struct Node{
  int cur,v,dis;
  bool operator<(const Node &rhs)const{
    return dis>rhs.dis;
  }
};
priority_queue<Node>q;
bool vis[11][N*10+20];
int dij(){
  memset(d,INF,sizeof(d));
  memset(vis,0,sizeof(vis));
  d[0][8*N+1]=0;
  q.push(Node{0,8*N+1,0});
  while(!q.empty()){
    int cur=q.top().cur,u=q.top().v;
    q.pop();
    if(vis[cur][u])continue;
    vis[cur][u]=1;
    for(int i=head[u];~i;i=edge[i].next){
       int v=edge[i].v;
       if(!vis[cur][v]&&d[cur][v]>d[cur][u]+edge[i].w){
        d[cur][v]=d[cur][u]+edge[i].w;
        q.push(Node{cur,v,d[cur][v]});
       }
       if(cur+1>k)continue;
       if(!vis[cur+1][v]&&d[cur+1][v]>d[cur][u]){
         d[cur+1][v]=d[cur][u];
         q.push(Node{cur+1,v,d[cur+1][v]});
       }
    }
  }
  return d[k][8*N+n]==INF?-1:d[k][8*N+n];
}
void build(int rt,int l,int r){
  if(l==r){
    add(N*8+l,rt,0);
    add(rt+4*N,N*8+l,0);
    return;
  }
  int mid=(l+r)>>1;
  build(rt<<1,l,mid);
  build(rt<<1|1,mid+1,r);
  add(rt<<1,rt,0),add(rt<<1|1,rt,0);
  add(rt+4*N,4*N+rt*2,0),add(rt+4*N,4*N+rt*2+1,0);
}
int now,w;
void treeadd1(int rt,int l,int r,int x,int y){
   if(x<=l&&r<=y){
      add(rt,now,w);
      return;
   }
   int mid=(l+r)>>1;
   if(x<=mid)treeadd1(rt<<1,l,mid,x,y);
   if(y>mid)treeadd1(rt<<1|1,mid+1,r,x,y);
}
void treeadd2(int rt,int l,int r,int x,int y){
   if(x<=l&&r<=y){
      add(now,rt+4*N,0);
      return;
   }
   int mid=(l+r)>>1;
   if(x<=mid)treeadd2(rt<<1,l,mid,x,y);
   if(y>mid)treeadd2(rt<<1|1,mid+1,r,x,y);
}
int main(){
  scanf("%d%d%d%d",&n,&n,&m,&k);
  memset(head,-1,sizeof(head));
  edge.clear();
  build(1,1,n);
  now=9*N;
  for(int i=1;i<=m;++i){
    int a,b,c,d;
    scanf("%d%d%d%d%d",&a,&b,&c,&d,&w);
    ++now;
    treeadd1(1,1,n,a,b);
    treeadd2(1,1,n,c,d);
    ++now;
    treeadd1(1,1,n,c,d);
    treeadd2(1,1,n,a,b);
  }
  int ans=dij();
  if(ans==-1)printf("CreationAugust is a sb!
");
  else printf("%d
",ans);
  return 0;
}
View Code
原文地址:https://www.cnblogs.com/shuguangzw/p/5414874.html