hdu5441(并查集+离线处理)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5441

题意:

  根据输入的三个整数n、m、q,表示有n座城市,m条路,q次询问。下面给出m行,每行三个数start、end、w,分别是这条道路的起点城市、终点城市、“权值”。然后给出q次询问的数值,每次询问的结果是符合条件的路有多少条。条件就是:如果这条路的权值比给的限制值要小,那么这条路就是可行的。注意就是:对于一条路的添加来说,只要a到b之间有路,那么符合条件的路就会有两条,一条是a到b,还有一条是b到a。比如:添加了两条路,a到b、b到c,那么其实答案就是6条,(a,b)、(a,c)、(b,a)、(b,c)、(c,a)、(c,b)。

思路:

  刚开始用常规的并查集做法来写,发现会TLE,看了下数据,我之前那种遍历方法绝对会超时啊……看到q次询问就不加思索的对每次询问进行遍历,这样每次询问就要遍历一遍那m组数据,虽然实际可能不用遍历完m组,但是这个时间复杂度是很高的。然后网上学习了下,发现有一种方法很好,那就是不光对load[i].w进行排序,还可以对这q次询问的限制值进行排序这样的话只需要对m组数据遍历一次就够了。貌似这种方法有个名称叫做“离线处理”?(我个人理解应该是把查询的值先读入,读入后不在线处理,而是先存起来,排序之后再在遍历m组数据的时候使用)因为输出的时候是要按照输入时的顺序对应输出,所以这里需要有个表示查询值编号的数据。然后计算公式的话,两个集合合并,有增量有减量,合并后原来那两个集合不存在了,减量就是n1*(n1-1)、n2*(n2-1);合并后新出现的新集合存在一个增量:(n1+n2)*(n1+n2-1)。

代码:

  1 #include<iostream>
  2 #include<algorithm>
  3 using namespace std;
  4
  5 const int maxn = 2e4 + 10;
  6 const int max_edge = 1e5 + 10;
  7 const int max_q = 5e3 + 10;
  8
  9 int fa[maxn];
 10 int num[maxn];    //记录这个集合中点的个数
 11
 12 struct Load
 13 {
 14     int start;
 15     int end;
 16     int w;
 17 } load[max_edge];
 18
 19 struct Qnode
 20 {
 21     int sum;
 22     int id;        //记录编号,便于最后输出时是按编号输出
 23 } qnode[max_q];
 24
 25 bool cmp1(Load a, Load b)        //按权值从小到大排序
 26 {
 27     return a.w < b.w;
 28 }
 29
 30 bool cmp2(Qnode a, Qnode b)        //按限制值从小到大排序
 31 {
 32     return a.sum < b.sum;
 33 }
 34
 35 void init(int n)
 36 {
 37     for(int i = 1; i <= n; i++)
 38     {
 39         fa[i] = i;
 40         num[i] = 1;
 41     }
 42     return;
 43 }
 44
 45 int find(int x)
 46 {
 47     if(fa[x] == x)
 48     {
 49         return x;
 50     }
 51     else
 52     {
 53         return fa[x] = find(fa[x]);
 54     }
 55 }
 56
 57 int main()
 58 {
 59     ios::sync_with_stdio(false);
 60     int t;
 61     int n, m, q;
 62
 63     long long ans[max_q];
 64
 65     cin >> t;
 66
 67     while(t--)
 68     {
 69         cin >> n >> m >> q;
 70         init(n);
 71         for(int i = 1; i <= m; i++)
 72         {
 73             cin >> load[i].start >> load[i].end >> load[i].w;
 74             //发现这个交换是多余的……
 75             /*if(load[i].start > load[i].end)
 76             {
 77                 swap(load[i].start, load[i].end);
 78             }*/
 79         }
 80         sort(load + 1, load + 1 + m, cmp1);
 81
 82         for(int i = 1; i <= q; i++)
 83         {
 84             cin >> qnode[i].sum;
 85             qnode[i].id = i;    //编号按顺序赋值
 86         }
 87         sort(qnode + 1, qnode + 1 + q, cmp2);
 88
 89         int cnt = 1;
 90         long long tmp = 0;
 91         for(int i = 1; i <= m; i++)    //这里是直接遍历路径数据,效率比我之前想的高多了……
 92         {
 93             int x = find(load[i].start);
 94             int y = find(load[i].end);
 95
 96             while(load[i].w > qnode[cnt].sum && cnt <= q)
 97             {
 98                 ans[qnode[cnt].id] = tmp;    //没有新的路添加,所以答案还是tmp,暂时不变
 99                 cnt++;    //当前这个小的qnode不能满足大于这条路的权值,那么就继续往下看比当前大的qnode是否符合条件
100             }
101             if(x != y)
102             {
103                 long long n1 = num[x], n2 = num[y];
104                 tmp += (n1 + n2) * (n1 + n2 - 1);
105                 tmp -= (n1 * (n1 - 1) + n2 * (n2 - 1));
106                 fa[x] = y;
107                 num[y] += num[x];    //x合并到y上,则x上点的个数也要加到y上
108             }
109         }
110         while(cnt <= q)    //这里的意思是,路径数据已经全部遍历完了,可能询问还没有结束,较小的询问已经处理过全部数据,那么较大的也一定能
111         {
112             ans[qnode[cnt++].id] = tmp;
113         }
114         for(int i = 1; i <= q; i++)
115         {
116             cout << ans[i] << endl;
117         }
118     }
119     return 0;
120 }

原文地址:https://www.cnblogs.com/friend-A/p/9416942.html

时间: 2024-11-05 23:27:13

hdu5441(并查集+离线处理)的相关文章

ACM学习历程—SNNUOJ 1110 传输网络((并查集 &amp;&amp; 离线) || (线段树 &amp;&amp; 时间戳))(2015陕西省大学生程序设计竞赛D题)

Description Byteland国家的网络单向传输系统可以被看成是以首都 Bytetown为中心的有向树,一开始只有Bytetown建有基站,所有其他城市的信号都是从Bytetown传输过来的.现在他们开始在其他城市陆 续建立了新的基站,命令“C x“代表在城市x建立了一个新的基站,不会在同一个城市建立多个基站:城市编号为1到n,其中城市1就是首都Bytetown.在建立基站的过程中他们还 会询问某个城市的网络信号是从哪个城市传输过来的,命令”Q x“代表查询城市x的来源城市. Inpu

BZOJ 1015 星球大战 并查集+离线

这道题说来真是艰辛,从一开始的RE,到RE,到刚刚的WA,再到AC. 这只能说明我进步的历程,还有我需要不断的加强努力.这道题思路不难,从很久前在黑书中并查集一节就能找到解题的踪影,因为并查集只能并,分不了,所以我们就得离线,倒过来写.只不过这道题真的得审好题目,它问的是剩下的星球中有多少个连通分量,不要搞错了.大概就是这个样子了,加油. 1 #include<cstdio> 2 #include<iostream> 3 #define rep(i,j,k) for(int i=

zoj 3261 Connections in Galaxy War(并查集+离线逆向操作)

 题目:给出一些点,每个点有权值,然后有一些边,相连.无向的.然后有一些操作 query a.表示从a出发的能到达的所有点权值最大的点的编号(相同取编号最小,而且权值要比自己大) destory a,b 表示删除连接a,b的边 思路并查集,但是要逆向处理,所以先离线读入,从后向前处理,于是对于destroy操作,等价于连接两个点的操作,然后对于每个询问输出即可 #include<cstdio> #include<cstring> #include<cmath> #i

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

搜了题解才把题搞明白.明白之后发现其实题意很清晰,解题思路也很清晰,只是题目表述的很不清晰…… 大意如下—— 给你一个无向图,图中任意两点的距离是两点间所有路径上的某一条边,这条边需要满足两个条件:1. 这条边这两点间某条路径上的最长边:2. 这条边是这两点间所有路径上的最长边中的最短边. 简单来说,假如a到d有两条路径,一条经过b,一条经过d,其中ab = 1, bd = 3, ac = 2, cd = 2,那么abd上的最长边为3,acd上的最长边为2,则ad的距离为2. 如果a, d两点间

【bzoj1015】【JSOI2008】【星球大战】【并查集+离线】

Description 非常久曾经.在一个遥远的星系,一个黑暗的帝国靠着它的超级武器统治者整个星系.某一天,凭着一个偶然的机遇,一支反抗军摧毁了帝国的超级武器.并攻下了星系中差点儿全部的星球.这些星球通过特殊的以太隧道互相直接或间接地连接. 但好景不长,非常快帝国又又一次造出了他的超级武器. 凭借这超级武器的力量.帝国開始有计划地摧毁反抗军占领的星球.因为星球的不断被摧毁,两个星球之间的通讯通道也開始不可靠起来.如今,反抗军首领交给你一个任务:给出原来两个星球之间的以太隧道连通情况以及帝国打击的

fzu 2059 并查集+离线处理

题意: There is a array contain N(1<N<=100000) numbers. Now give you M(1<M<10000) query. Every query will be: 1 x : ask longest substring which every number no less than x 2 y x : change the A[y] to x. there are at most change 10 times. For each

计蒜客 444 / xtuoj 1024 京东的物流路径(并查集+离线lca)

题意:一颗树,定义一条路径的权值等于路径的边权之和,需要求这颗树所有路径中权值的最大值 思路: 考虑到路径权值与点权的最值有关,而最值的问题通常可以通过排序就行处理,于是想到先把点权排序. 容易看出如果某条路径的权值是通过某个点算出的最小 ,那么肯定这条路径肯定不会经过权值更小的点,于是有了两种处理思路 1.按点权从小到大删点,对于即将删除的点,比他权值小的点已经被删去了,所以只要在当前状态的森林里找一条最长路径乘以次点权就可以更新答案 2.按点权从大到小加点,显然新加进来的点权值最小,当前树里

HDU 3938:Portal(并查集+离线处理)

http://acm.hdu.edu.cn/showproblem.php?pid=3938 Portal Problem Description ZLGG found a magic theory that the bigger banana the bigger banana peel .This important theory can help him make a portal in our universal. Unfortunately, making a pair of port

bzoj1015: [JSOI2008]星球大战starwar 并查集+离线处理

题目传送门 这道题可以改为离线处理 倒着找答案 这样删点就变成加点了 有了这个思想题目就很好写了哇 23333 #include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int M=400007; int read(){ int ans=0,f=1,c=getchar(); while(c<'0'||c>'9'){if(c=='-') f=-1; c=g