貌似BNUOJ只能用这个链接去了http://oj.51isoft.com/bnuoj/problem_show.php?pid=12884
还是把题目复制过来,免得挂了找不到题吧。
灯泡队
Time Limit: 10000 ms Case Time Limit: 10000 ms Memory Limit: 65535 KB
Description
众所周知,ACM是一项三人组队的团体比赛。由于其奇葩的队伍人数,形成了很多一对情侣+一个灯泡的灯泡队,如北航的BUAA_OhMyGold队。当然~北师也有这样的灯泡队~比如拿到北师第二块金、第三块金的队伍~再比如去年新人队:小胖 + lmm199104 + yuxinpei的“胖黎于”组合。从小胖同学处得知,另外两人(关系)“感觉挺好啊 估计都能睡一起~”。(PS.以上纯属恶搞,一切后果由小胖同学负责~)
某天,yuxinpei想找lmm199104玩。已知北师大可以被划分N个区域(将其编号为1,2,…N),二人现在在两个不同的区域。这N个区域由M条路(无向边)连接,每条路都会连接两个区域。通过这M条路,任意两个区域之间都可以相互到达(图是联通的)。每条路都会有一个固定的人流量,由于北师大很小人又很多,因此路上经常塞满了人。一个路径(一系列相连的路,如a->b->d->c->a->c)的拥挤程度为这条路径上所有路的人流量的最大值。yuxinpei希望知道他去找lmm199104的所有路径中拥挤程度的最小值是多少。
Input
输入的第一行为一个正整数T表示数据组数(T≤20)。接下来的T组数据,每组的第一行为两个数N和M(2≤N≤50000, 1≤M≤10^5)。接下来的M行每行三个数xi,yi,di(1≤xi, yi≤N, xi≠yi, 0≤di<10^9)表示第xi号区域与第yi号区域之间有一条人流量为di的路。接下来的一行为一个数q表示询问的次数(1≤q≤50000),接下来的q行每行两个数si, ti (1≤si, ti≤N, si≠ti),表示现在两人分别在第si号区域和第ti号区域,需要求出这种情况下的最小拥挤度。
Output
对于每个询问,输出一个整数表示问题的答案。
Sample Input
2
4 5
1 2 1
1 3 2
1 4 10
4 3 1
2 4 3
2
1 4
4 1
2 1
1 2 100
1
1 2
Sample Output
2
2
100
最小生成树+LCA+倍增
对于75分钟的比赛而言,这道属于比较imba的题。
做过Frogger(http://acm.bnu.edu.cn/bnuoj/problem_show.php?pid=1014)的同学应该觉得这道很眼熟,两道题问的东西基本是一样的,只不过这题的询问量比较大。
第一步比较容易想到:做出这张图的最小生成树,则最优路径就是树上两点之间的路径(有多个最小生成树时任意一棵都满足这一点)。于是问题转化为求树上两点间的最大边。
学过如何求树上两点间距离的同学应该会想到LCA,将问题转化为两点及最近公共祖先到根的问题。不过由于取小并不像求和可以直接做减法,因此无法直接转化过去。
设两点分别为x、y,他们的最近公共祖先为z,我们只要知道x到z的路径的最大值和y到z的路径的最大值就可以了。于是想到倍增,b[x][i]表示从x往根方向走2^i步所经过的最大值。进而每次询问可以O(logN)给出答案。
做出这张图的最小生成树,则最优路径就是树上两点之间的路径,然后用扩展TARJAN求树上两点间最大边了。可以在并查集合并时更新最值。
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int N=55555,M=111111; #define clr(a,x) memset(a,x,sizeof(a)) struct LCA { int ev[1<<20],ew[1<<20],nxt[1<<20]; int he[N],hq[M],ha[M],e; int vis[N],p[N],val[N],ans[N]; void init() { clr(ha,-1);clr(val,0xa3);e=0; clr(he,-1),clr(hq,-1),clr(vis,0); } int find(int x) { static int path[N]; int len=0,px,i; for(px=x;~p[px];px=p[px]) path[len++]=px; for(int i=len-1;i>=0;i--) { int u=path[i]; val[u]=max(val[u],val[p[u]]); p[u]=px; } return px; } void unio(int x,int y,int w) { p[find(y)]=find(x); val[y]=w; } void add(int *h,int u,int v,int w=0) { ev[e]=v,ew[e]=w,nxt[e]=h[u];h[u]=e++; if(h==ha) return; ev[e]=u,ew[e]=w,nxt[e]=h[v];h[v]=e++; } void dfs(int u) { vis[u]=1,p[u]=-1; for(int i=he[u];~i;i=nxt[i]) if(!vis[ev[i]]) dfs(ev[i]),unio(u,ev[i],ew[i]); vis[u]=2; for(int i=hq[u];~i;i=nxt[i]) if(vis[ev[i]]==2) add(ha,find(ev[i]),i,ew[i]); for(int i=ha[u];~i;i=nxt[i]) { int x=ev[ev[i]],y=ev[ev[i]^1]; find(x),find(y); ans[ew[i]]=max(val[x],val[y]); } } }lca; struct Edge { int u,v,w;Edge(){} Edge(int u,int v,int w):u(u),v(v),w(w){} bool operator < (const Edge &p) const {return w<p.w;} }e[M]; int p[N],r[N]; int find(int x) { return ~p[x]?p[x]=find(p[x]):x; } int unio(int x,int y) { x=find(x),y=find(y); if(x==y) return -1; return p[x]=y; } int main() { int t,n,m; scanf("%d",&t); while(t--) { lca.init(); clr(p,-1),clr(r,0); scanf("%d%d",&n,&m); for(int i=0;i<m;i++) scanf("%d%d%d",&e[i].u,&e[i].v,&e[i].w); sort(e,e+m); for(int i=0;i<m;i++) if(~unio(e[i].u,e[i].v)) lca.add(lca.he,e[i].u,e[i].v,e[i].w); int q,x,y; scanf("%d",&q); for(int i=0;i<q;i++) scanf("%d%d",&x,&y),lca.add(lca.hq,x,y,i); lca.dfs(1); for(int i=0;i<q;i++) printf("%d\n",lca.ans[i]); } return 0; }