BNUOJ 12884 kruskal+ext_lca

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


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值