码迷,mamicode.com
首页 > 其他好文 > 详细

hdu 3938 Portal(并查集+离线+kruskal)

时间:2015-07-16 23:57:00      阅读:138      评论:0      收藏:0      [点我收藏+]

标签:

搜了题解才把题搞明白。明白之后发现其实题意很清晰,解题思路也很清晰,只是题目表述的很不清晰……

大意如下——

给你一个无向图,图中任意两点的距离是两点间所有路径上的某一条边,这条边需要满足两个条件:1. 这条边这两点间某条路径上的最长边;2. 这条边是这两点间所有路径上的最长边中的最短边。

简单来说,假如a到d有两条路径,一条经过b,一条经过d,其中ab = 1, bd = 3, ac = 2, cd = 2,那么abd上的最长边为3,acd上的最长边为2,则ad的距离为2。

如果a, d两点间的距离小于能量L,那么就可以在a, d两点间建立一个传送门。

现在,求在L的能量下最多可以在这个图中建立多少个传送门。

 

输入:

多组输入数据。

每组输入数据第一行包括三个整数n, m, q。表示节点数,边数,请求数。

接下来m行,每行三个整数u, v, val,表示边的源点,目的点,边权(注意,是无向图,源点和目的点等价)。

接下来q行,每行一个整数L,表示请求所提供的能量。

 

解题核心:如果集合x与集合y不连通,而此时有一条路L‘将x与y连通,且L‘ <= L,此时将可以建立新传送门num[x]*num[y]个,num[x]表示x集合中的节点数。L1连通后,将集合x与集合y合并,得到新集合x,num[x] += num[y],这就是并查集。

可以使用并查集+kruskal进行求解。即,将所有边从小到大排序,每次按顺序向并查集中增加新边,需要保证添加的新边不会构成环,直到边长>请求所提供的能量L。

新问题出现了,当我们在L1的能量下将路径求出来了,那么如果下一次请求能量为L2,那么我们无法在已有的并查集上继续求解,只能重新建立并查集,这将产生极大的浪费。所以,我们需要将请求L1——Lq全部记录下来,即离线操作,然后按照从小到大的顺序进行求解。最后在将解按照请求顺序排序输出。

 

上代码——

  1 #include <cstdio>
  2 #include <cstring>
  3 #include <cmath>
  4 #include <algorithm>
  5 using namespace std;
  6 
  7 const int M = 10010;
  8 
  9 struct Que                                  //保存查询
 10 {
 11     int q, id, ans;                         //分别是查询值,查询顺序,输出结果
 12 }que[M];
 13 
 14 struct Edge                                 //保存边
 15 {
 16     int u, v, val;
 17 }edge[5*M];
 18 
 19 int fm[M];                                  //并查集使用
 20 int sum[M];                                 //记录各区间节点数
 21 
 22 int n, m, q;
 23 
 24 bool cmp(Edge x, Edge y)
 25 {
 26     return x.val <= y.val;
 27 }
 28 
 29 bool cmp1(Que x, Que y)
 30 {
 31     return x.q <= y.q;
 32 }
 33 
 34 bool cmp2(Que x, Que y)
 35 {
 36     return x.id < y.id;
 37 }
 38 
 39 void init()
 40 {
 41     for(int i = 1; i <= n; i++)
 42     {
 43         fm[i] = i;
 44         sum[i] = 1;
 45     }
 46     for(int i = 0; i < m; i++) scanf("%d%d%d", &edge[i].u, &edge[i].v, &edge[i].val);
 47     sort(edge, edge+m, cmp);                            //按路径长度从小到大排序
 48 
 49     for(int i = 0; i < q; i++)
 50     {
 51         scanf("%d", &que[i].q);
 52         que[i].id = i;
 53         que[i].ans = 0;
 54     }
 55     sort(que, que+q, cmp1);                             //按请求长度从小到大排序
 56 }
 57 
 58 int mfind(int x)                                        //查询操作,含路径压缩
 59 {
 60     int fx = x;
 61     while(fx != fm[fx]) fx = fm[fx];
 62     while(x != fm[x])
 63     {
 64         int mid = fm[x];
 65         fm[x] = fx;
 66         x = mid;
 67     }
 68     return fx;
 69 }
 70 
 71 void work()
 72 {
 73     int cnt = 0;
 74     for(int i = 0; i < q; i++)                          //回应请求
 75     {
 76         while(que[i].q >= edge[cnt].val && cnt < m)     //kruskal算法
 77         {
 78             int fx = mfind(edge[cnt].u);
 79             int fy = mfind(edge[cnt].v);
 80             if(fx != fy)
 81             {
 82                 que[i].ans += sum[fx]*sum[fy];          //新增传送阵
 83                 fm[fy] = fx;                            //集合合并
 84                 sum[fx] += sum[fy];
 85             }
 86             cnt++;
 87         }
 88         if(i > 0) que[i].ans += que[i-1].ans;           //包含已有传送阵
 89     }
 90 }
 91 
 92 void output()
 93 {
 94     sort(que, que+q, cmp2);                             //按请求顺序排序
 95     for(int i = 0; i < q; i++) printf("%d\n", que[i].ans);
 96 }
 97 
 98 int main()
 99 {
100     //freopen("test.txt", "r", stdin);
101     while(~scanf("%d%d%d", &n, &m, &q))
102     {
103         init();
104         work();
105         output();
106     }
107     return 0;
108 }

 

hdu 3938 Portal(并查集+离线+kruskal)

标签:

原文地址:http://www.cnblogs.com/mypride/p/4652521.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!