<最小生成树><lca>Heatwave
2015-10-02 23:25
337 查看
题目
给你N个点的无向连通图,图中有M条边,第j条边的长度为: djdj现在有 K个询问。每个询问的格式是:A B,表示询问从A点走到B点的所有路径中,最长的边最小值是多少?数据范围
50% 1<=N,M<=3000其中30% K<=5000
100% 1 <= N <= 15,000 1 <= M <= 30,000 1 <= dj<= 1,000,000,000 1 <= K <= 20,000
分析
从题目看,有一句话很重要:表示询问从A点走到B点的所有路径中,最长的边最小值是多少?”所有“这词提醒了我们这些路径中有很多是无用的,是浪费的。我们会发现这题要求的条件也很特殊,也就是只要是连通的路径,就可以取里面的最大值了。再者,既然最大值最小,那么我们可以先使这些选出来的路径尽量的小,且可以使他们连通就可以了,这就变成了一棵树,那么也就是说这n个点中我们可以先取n-1条边,使得这n-1条边可以代表这些其他边,还有要最小,自然而然就是最小生成树了!做完最小生成树后这个图就变成一棵树,然后再处理这棵树每两个点之间的最大边的值,也就是用lca+rmqlca+rmq处理,便可以使复杂度变成NlognN log n的。补充kruskal
即贪心地选n-1条边,使得可以让全部点连通。就是用并查集来判断连通,快排边权,再连。简单来说就是:
[code] qsort(1,n); for i=1 to n do if b[i][1]与b[i][2]不连通{ 连双向边b[i][1]-b[i][2]; 合并b[i][1]和b[i][2]的子集; }
补充最长公共祖先
首先要求一次dfs序,使得整棵树有编号。再RMQRMQ去处理每个点往上的2j2^j个点的编号,这时我们就可以先把x,yx,y调成一个深度即[code] lca:=0; if d[x]<d[y] then begin k:=x;x:=y;y:=k;end; k:=trunc(ln(d[x]-d[y]+1)/ln(2)); while k>=0 do begin if d[g[x,k]]>d[y] then begin lca:=max(lca,wer[x,k]);x:=g[x,k]; end; dec(k); end;
然后就开始找到一个公共的祖先就可以了:
[code]if d[x]<>d[y] then begin lca:=max(lca,wer[x,0]);x:=g[x,0];end; k:=trunc(ln(d[x])/ln(2)); while k>=0 do begin if g[x,k]<>g[y,k] then begin lca:=max(max(lca,wer[x,k]),wer[y,k]);x:=g[x,k];y:=g[y,k]; end; dec(k); end; if x=y then exit(lca) else exit(max(lca,max(wer[x,0],wer[y,0]))); end;
其实就是一直往上跳的一个过程。
注意
在求两点之间的最小边权时,可以在求最长公共祖先时同时求。[code]var a:array[0..30000,1..3] of longint; i,n,m,k,gx,gy,op,x,y,num,j:longint; fa,d:array[0..15000] of longint; b,next,last,f:array[1..80000] of longint; g,wer:array[0..15000,0..16] of longint; function max(l,r:longint):longint; begin if l<r then exit(r) else exit(l); end; procedure qsort(l,r:longint); var i,j,mid:longint; begin i:=l;j:=r;mid:=a[(l+r)shr 1,3]; repeat while a[i,3]<mid do inc(i); while a[j,3]>mid do dec(j); if i<=j then begin a[0]:=a[i];a[i]:=a[j];a[j]:=a[0];inc(i);dec(j); end; until i>j; if l<j then qsort(l,j); if i<r then qsort(i,r); end; function get(k:longint):longint; begin if fa[k]=0 then exit(k) else begin fa[k]:=get(fa[k]); exit(fa[k]); end; end; procedure insert(x,y,z:longint); begin inc(num); b[num]:=y; next[num]:=last[x]; last[x]:=num; f[num]:=z; end; procedure dfs(k,y:longint); var p:longint; begin p:=last[k]; while p<>0 do begin if b[p]<>y then begin g[b[p],0]:=k; d[b[p]]:=d[k]+1; wer[b[p],0]:=f[p]; dfs(b[p],k); end; p:=next[p]; end; end; function lca(x,y:longint):longint; var k:longint; begin lca:=0; if d[x]<d[y] then begin k:=x;x:=y;y:=k;end; k:=trunc(ln(d[x]-d[y]+1)/ln(2)); while k>=0 do begin if d[g[x,k]]>d[y] then begin lca:=max(lca,wer[x,k]);x:=g[x,k]; end; dec(k); end; if d[x]<>d[y] then begin lca:=max(lca,wer[x,0]);x:=g[x,0];end; k:=trunc(ln(d[x])/ln(2)); while k>=0 do begin if g[x,k]<>g[y,k] then begin lca:=max(max(lca,wer[x,k]),wer[y,k]);x:=g[x,k];y:=g[y,k]; end; dec(k); end; if x=y then exit(lca) else exit(max(lca,max(wer[x,0],wer[y,0]))); end; begin readln(n,m,k); for i:=1 to m do readln(a[i,1],a[i,2],a[i,3]); qsort(1,m); for i:=1 to m do if get(a[i,1])<>get(a[i,2]) then begin insert(a[i,1],a[i,2],a[i,3]); insert(a[i,2],a[i,1],a[i,3]); gx:=get(a[i,1]); gy:=get(a[i,2]); fa[gx]:=gy; end; d[1]:=1; dfs(1,0); for j:=1 to trunc(ln(n)/ln(2)) do for i:=1 to n do begin g[i,j]:=g[g[i,j-1],j-1]; wer[i,j]:=max(wer[i,j-1],wer[g[i,j-1],j-1]); end; for i:=1 to k do begin readln(x,y); writeln(lca(x,y)); end; end.
相关文章推荐
- HTML初步学习9
- Hibernate HQL 单表查询
- Android 调用继承application的类
- 把所有界面的状态栏字体颜色设置为白色--iOS开发系列---项目中成长的知识一
- [BZOJ2194]快速傅立叶之二
- noip2012 同余方程 (扩展欧几里得)
- [leetcode]Best Time to Buy and Sell Stock III
- 【C++】c++实现线性表、链表
- java io流系统介绍
- 2014西安F题
- HTTP内建Authentication机制分析
- 程序自动分析(prog)
- 【C++知识点】可重载与不可重载的操作符
- leetcode笔记:Trapping Rain Water
- 2015第40周五
- 正则表达式记录1
- noip2012 开车旅行 (倍增处理)
- 远程桌面无法复制粘贴
- 9.Python进阶_动态类型
- USACO 2.3 Zero Sum