【最短路径树+可并堆/树链剖分】BZOJ1576 [Usaco2009 Jan]安全路经Travel
来源:互联网 发布:nba总决赛数据统计 编辑:程序博客网 时间:2024/05/22 01:46
题面在这里
此题最重要的思想就是建立“最短路径树”。
何为最短路径树?就是最短路经过的所有点和边构成的一棵树(以前竟然没有发现这个东西)
考虑不经过最短路的最后一条边,其实就是把点x到父亲的边去掉
那么剩下的最短路径必定是这样的:
由一条不在树上的边(u,v)连接x子树和其余部分,则路径为1~u~v~x
可以发现,整个过程只走了一次不在树上的边,这样就保证了答案必定最优
这个方案的答案为:dst[u]+dst[v]+w(u,v)-dst[x]
然后就有两种解法:
【可并堆】
对于每棵子树,建立一个小根堆维护dst[u]+dst[v]+w(u,v)的最小值即可
从叶往根处理,每次合并各个儿子的堆,为了效率用可并堆(当然你也可以启发式合并一发)这里我用左偏树实现
就解决了,时间复杂度
附上代码:
#include<cstdio>#include<queue>#include<cstring>#include<algorithm>using namespace std;const int maxn=100005,maxe=400005;struct data{ int id,w,fa; data (int x,int y,int z): id(x),w(y),fa(z) {} bool operator<(const data&b)const{ return w>b.w; }};int n,e,ans[maxn],dst[maxn],fa[maxn];struct edge{ int tot,lnk[maxn],son[maxe],nxt[maxe],w[maxe]; edge() {tot=0;} void add(int x,int y,int wi){ son[++tot]=y;nxt[tot]=lnk[x];w[tot]=wi;lnk[x]=tot; }}a,b;inline int red(){ int tot=0,f=1;char ch=getchar(); while (ch<'0'||'9'<ch) {if (ch=='-') f=-f;ch=getchar();} while ('0'<=ch&&ch<='9') tot=tot*10+ch-48,ch=getchar(); return tot*f;}int getfa(int x){ if (fa[x]==x) return x; return fa[x]=getfa(fa[x]);}priority_queue<data> Q;bool vis[maxn];void DIJ(){ memset(dst,63,sizeof(dst)); dst[1]=0;Q.push(data(1,0,1)); while (!Q.empty()){ while (!Q.empty()&&vis[Q.top().id]) Q.pop(); if (Q.empty()) break;int k=Q.top().id,fa=Q.top().fa;Q.pop(); if (k!=1) b.add(fa,k,dst[k]-dst[fa]); vis[k]=1; for (int j=a.lnk[k];j;j=a.nxt[j]) if (dst[a.son[j]]>dst[k]+a.w[j]) dst[a.son[j]]=dst[k]+a.w[j], Q.push(data(a.son[j],dst[a.son[j]],k)); }}struct node{ int l,r,w,d,id; bool operator<(const node&b)const{ return w<b.w; } node() {} node(int x,int i):w(x),id(i),d(0),l(0),r(0) {}}tre[maxe];int rot[maxn],len;int merge(int a,int b){ if (!a||!b) return a+b; if (tre[b]<tre[a]) swap(a,b); tre[a].r=merge(tre[a].r,b); if (tre[tre[a].l].d<tre[tre[a].r].d) swap(tre[a].l,tre[a].r); if (!tre[a].r) tre[a].d=0;else tre[a].d=tre[tre[a].r].d+1; return a;}int unite(int x,int y){ int fx=getfa(x),fy=getfa(y); fa[fx]=fy;}void dfs(int x,int father){ for (int j=b.lnk[x];j;j=b.nxt[j]) dfs(b.son[j],x),fa[x]=unite(x,b.son[j]); rot[x]=0; for (int j=a.lnk[x];j;j=a.nxt[j]) if (a.son[j]!=father&&getfa(a.son[j])!=getfa(x)) tre[++len]=node(dst[a.son[j]]+dst[x]+a.w[j],a.son[j]),rot[x]=merge(rot[x],len); for (int j=b.lnk[x];j;j=b.nxt[j]) rot[x]=merge(rot[x],rot[b.son[j]]); while (getfa(tre[rot[x]].id)==getfa(x)) rot[x]=merge(tre[rot[x]].l,tre[rot[x]].r); ans[x]=tre[rot[x]].w-dst[x];}int main(){ n=red(),e=red(); for (int i=1;i<=n;i++) fa[i]=i; for (int i=1,x,y,w;i<=e;i++) x=red(),y=red(),w=red(),a.add(x,y,w),a.add(y,x,w); DIJ();len=0; dfs(1,1); for (int i=2;i<=n;i++) if (ans[i]<0) printf("-1\n");else printf("%d\n",ans[i]); return 0;}
【树链剖分】
想法与上面类似
我们枚举每条边(u,v),假设u在x子树外,v在x子树内
则可以用dst[u]+dst[v]+w(u,v)来更新x~v路径上所有点的最小值
这样就可以用树链剖分+线段树来维护这个最小值了
最后输出答案时再-dst[i]
想法相似,所以没有配代码……
3 0
- 【最短路径树+可并堆/树链剖分】BZOJ1576 [Usaco2009 Jan]安全路经Travel
- 【BZOJ1576】[Usaco2009 Jan]安全路经Travel【最短路树】【树链剖分】【线段树】
- 1576: [Usaco2009 Jan]安全路经Travel 最短路径树+树链剖分+线段树
- 【bzoj1576】[Usaco2009 Jan]安全路经Travel
- BZOJ1576: [Usaco2009 Jan]安全路经Travel
- [bzoj1576] [Usaco2009 Jan]安全路经Travel
- [BZOJ1576] [Usaco2009 Jan]安全路经Travel
- [BZOJ1576][Usaco2009 Jan]安全路径Travel(堆优化dijkstra+并查集)
- bzoj1576[Usaco2009 Jan]安全路径Travel(堆优化dijkstra+并查集)
- [Usaco2009 Jan]安全路经Travel(最短路树+并查集/树链剖分)
- BZOJ1576——[Usaco2009 Jan]安全路经Travel
- BZOJ1576 洛谷P2934 : [Usaco2009 Jan]安全路经Travel
- BZOJ 1576: [Usaco2009 Jan]安全路经Travel【最短路树】【树链剖分】
- bzoj 1576: [Usaco2009 Jan]安全路经Travel(dijkstra堆优化+并查集)
- [Usaco2009 Jan]安全路经Travel dijkstra + 并查集
- [BZOJ1576]安全路径travel-最短路径树+并查集
- 【BZOJ 1576】 [Usaco2009 Jan]安全路经Travel
- bzoj 1576 [Usaco2009 Jan]安全路经Travel
- Response.Redirect()和Server.Transfer()和Server.Execute()的区别
- java的新生代 老年代 永久代
- bootstrap实现多行折叠
- opengl es 2.0 视频播放
- VS启动时不显示最近打开项目的解决办法
- 【最短路径树+可并堆/树链剖分】BZOJ1576 [Usaco2009 Jan]安全路经Travel
- ORACLE关于返回结果集的问题
- ZooKeeper伪分布式集群安装及使用
- postgresq通过l外部表连接greenplum
- images
- 关于新手学习嵌入式linux
- MyEclipse使用JUnit
- POJ NOI0105-34 求阶乘的和(Bailian2684)
- spark一千篇旅游日记0005 之 DataFrame(一)