先从一道例题开始:CodeForces - 786B Legacy

题意:

  有n(≤105)个点,q(≤105)个操作,每个操作为以下三种操作之一:

  操作一:u到[l,r]之间的点建一条边权为w的有向边;

  操作二:[l,r]的点到u之间建一条边权为w的有向边;

  操作三:u到v建一条边权为w的有向边;

  求点到其他点的最短路,无法到达的点输出-1。

分析:

  此题的难点在于建边。对于操作一和操作二,直接建边O(n2),肯定会TLE。

  考虑到需要对区间进行操作,我们可以利用利用线段树的结构来优化建边。具体做法如下:

  一、建立两棵线段树(全是虚点),一棵由父亲向儿子连边,一棵由儿子向父亲连边,边权都为0,两棵树的叶节点都编号为1-n。

  二、对于操作一和操作二,利用线段树拆分区间,之后从u向拆分后的区间对应的虚点连边,边权为w

  如此一来,建边的效率应该可以优化到O(log(n))。

  放上核心代码:

void build(int p,int l,int r){
    t[p].l=o[p].l=l;t[p].r=o[p].r=r;
    if(l==r){t[p].pos=o[p].pos=l;return;}
    int mid=(l+r)>>1;
    t[p].pos=++cnt;o[p].pos=++cnt;
    build(lc,l,mid);build(rc,mid+1,r);
    add(t[p].pos,t[lc].pos,0);add(t[p].pos,t[rc].pos,0);
    add(o[lc].pos,o[p].pos,0);add(o[rc].pos,o[p].pos,0);
}
void admi(int p,int l,int r,int u,LL dis,int opt){//此题数据比较大记得开long long(代码中已将其定义为LL)
    if(l<=t[p].l&&t[p].r<=r){
        if(opt==2) add(u,t[p].pos,dis);
        else if(opt==3) add(o[p].pos,u,dis);
        return;
    }
    int mid=(t[p].l+t[p].r)>>1;
    if(l<=mid) admi(lc,l,r,u,dis,opt);
    if(r> mid) admi(rc,l,r,u,dis,opt);
}

 

例题:WOJ4578 Journeys

题意:

  有n(n≤5*105)个点,m(n≤105)个操作,每个操作将[l1,r1]之间的点与[l2,r2]之间的点一一建边(无向边)。求起点到所有点所经过的最少边数(保证起点可以到打所有点)。

分析:

  跟上一道题差不多。考虑用线段树优化建图,但是发现区间和区间之间不好连边。怎么办?

  添加虚点。

  我们先把无向边拆成两条有向边,对于每条有向边,我们都设置一个虚点,于是问题就转化成了由区间向虚点连边,再由虚点向区间连边(这不就是上一题吗?)。为了方便计算,我们把每条边去设为1,这样在最后输出答案时对每个点到起点的最短路除以2即可得到正确的答案。

  放上代码:

#include<bits/stdc++.h>
using namespace std;
#define N 8000010
#define INF 0x3f3f3f3f
#define lc (p<<1)
#define rc (p<<1|1)
int n,m,st,cnt,num,d[N],f[N],vis[N];
struct node{
    int u,v,w,nxt;
}e[N];
struct tree{
    int l,r,pos;
}t[N],o[N];
void add(int u,int v,int w){e[++num]=(node){u,v,w,f[u]};f[u]=num;}
void build(int p,int l,int r){
    t[p].l=o[p].l=l;t[p].r=o[p].r=r;
    if(l==r){t[p].pos=o[p].pos=l;return;}
    int mid=(l+r)>>1;
    t[p].pos=++cnt;o[p].pos=++cnt;
    build(lc,l,mid);build(rc,mid+1,r);
    add(t[p].pos,t[lc].pos,0);add(t[p].pos,t[rc].pos,0);
    add(o[lc].pos,o[p].pos,0);add(o[rc].pos,o[p].pos,0);
}
void admi(int p,int l,int r,int u,int opt){
    if(l<=t[p].l&&t[p].r<=r){
        if(opt==0) add(u,t[p].pos,1);
        else if(opt==1) add(o[p].pos,u,1);
        return;
    }
    int mid=(t[p].l+t[p].r)>>1;
    if(l<=mid) admi(lc,l,r,u,opt);
    if(r> mid) admi(rc,l,r,u,opt);
}
typedef pair<int,int> T;
void dijkstra(int s){
    memset(vis,0,sizeof(vis));
    memset(d,0x3f,sizeof(d));
    priority_queue< T,vector<T>, greater<T> >q;
    d[s]=0;q.push(make_pair(d[s],s));
    while(!q.empty()){
        pair<int,int> t=q.top();q.pop();
        int dd=t.first,u=t.second;
        if(vis[u]) continue;vis[u]=1;
        for(int i=f[u];i;i=e[i].nxt){
            int v=e[i].v,w=e[i].w;
            if(d[v]>dd+w){d[v]=dd+w;q.push(make_pair(d[v],v));}
        }
    }
}
int main(){
    int x,y,s,t;
    scanf("%d%d%d",&n,&m,&st);cnt=n;build(1,1,n);
    for(int i=1;i<=m;i++){
        scanf("%d%d%d%d",&x,&y,&s,&t);
        cnt++;admi(1,x,y,cnt,0);admi(1,s,t,cnt,1);
        cnt++;admi(1,x,y,cnt,1);admi(1,s,t,cnt,0);
    }
    dijkstra(st);
    for(int i=1;i<=n;i++) printf("%d\n",d[i]/2);
    return 0;
}