首页 > 代码库 > hdu3938 Portal 离线的并查集
hdu3938 Portal 离线的并查集
离线算法是将全部输入都读入,计算出所有的答案以后再输出的方法。主要是为避免重复计算。类似于计算斐波那契数列的时候用打表的方法。
题目:给一个无向图,求有多少个点对,使得两点间的路径上的花费小于L,这里路径上的花费是这样规定的,a、b两点之间所有的路径中的最大边的最小值。
当然题目上不是这么写的。它问的是有多少种路径,这里就比较模糊了,到底两个路径怎样才算是两种路径呢,这时候重新看题,可以发现,如果理解为路径中经过的点不同的话,题目中给的所谓两点间的花费这个定义就没有意义了,所以就可以猜测,题目要求的是有多少个点对了。
然后就明确题意后,再进行分析。对一个点对的所有路径,只要最短最大边的那条路径出现,其后的所有较大最大边的路径都是毫无意义的,那么不妨将所有的边按照权值从小对大进行排序,用并查集的方法,进行加边,已经连通的点就不再管。那么每次加边的时候,是将两个集合并在一起的过程,假设集合大小分为a,b,显然路径的种类是a*b个,此时对于所有大于集合中最大边的L,都要加上这个种类数了。
那么,算法就明确了,为离线算法,先输入所有的边和L,对所有的L进行排序,对所有的边进行排序,都为从小到大,然后对每个L,将比其小的边权的边都并在一起,计算种类数即可。
上面的两段来源于:http://blog.csdn.net/sdj222555/article/details/7439187
有点感慨,如果没有读懂题目,真的无法做题。
#include<iostream>#include<cstdio>#include<cstring>#include<algorithm>using namespace std;const int N=10010,M=50010;struct node{ int u,v,w;}edge[M];bool cmp(const node &a, const node &b){ return a.w<b.w;}int f[N],r[N];int n,m,q,cnt;void init(){ for(int i=0;i<=n;i++) { f[i]=i;r[i]=1; }}int Find(int x){ if(x==f[x]) return x; return f[x]=Find(f[x]);}void Link(int x,int y){ int a=Find(x), b=Find(y); cnt=0; if(a!=b) { f[b]=a; cnt=r[a]*r[b]; r[a]+=r[b]; }}int ans[N];struct NODE{ int id,re,num;}que[N];bool cmp1(const NODE &a,const NODE &b){ return a.num<b.num;}bool cmp2(const NODE &a,const NODE &b){ return a.id<b.id;}int main(){ //freopen("test.txt","r",stdin); int i,j,k; while(scanf("%d%d%d",&n,&m,&q)!=EOF) { init(); for(i=0;i<m;i++) scanf("%d%d%d",&edge[i].u,&edge[i].v,&edge[i].w); for(i=0;i<q;i++) { scanf("%d",&que[i].num); que[i].id=i; que[i].re=0; } sort(edge,edge+m,cmp); sort(que,que+q,cmp1); k=0; for(i=0;i<q;i++) { while(edge[k].w<=que[i].num&&k<m) { int a=Find(edge[k].u), b=Find(edge[k].v); if(a==b) {k++; continue;} else { Link(edge[k].u,edge[k].v); que[i].re+=cnt; k++; } } if(i>0) que[i].re+=que[i-1].re; } sort(que,que+q,cmp2); for(i=0;i<q;i++) printf("%d\n",que[i].re); } return 0;}
hdu3938 Portal 离线的并查集
声明:以上内容来自用户投稿及互联网公开渠道收集整理发布,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任,若内容有误或涉及侵权可进行投诉: 投诉/举报 工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。