貌似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;
}