先从一道例题开始: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; }