hdu5441(2015长春网络赛E题)

来源:互联网 发布:网络贷款需要什么手续 编辑:程序博客网 时间:2024/05/04 21:42

题意:

给出一个n个点、m条边的无向图,边上有权值,有q组询问,每组询问给出一个数字x,我们要在图中找出‘点对’的个数,这些‘点对’(例如a,b)满足从a到b有一条路径经过的每一条边都要小于x,输出每组询问点对的数量。


思路:

并查集。

我们把查询操作离线化,把边按权值从小到大排序,把查询操作也从小到大排序,每次查询操作都扫一遍边,判断满足的边的端点在不在同一个并查集中,如果不在就合并这两个并查集,同时记录新增加的数量,扫到不满足的边就跳出,继续下一次查询操作。


代码:

#include<cstdio>#include<cstring>#include<iostream>#include<algorithm>using namespace std;struct Edge{int u,v,x;}e[100005];int cmp(Edge a,Edge b){return a.x<b.x;}struct Q{int pos,x;}query[5005];int cmp1(Q a,Q b){return a.x<b.x;}int ans[5005];int fa[200005];int num[200005];int n,m,q;void init(){for(int i=0;i<=n;i++){fa[i]=i;num[i]=1;}return ;}int find1(int x){return fa[x]==x? x: fa[x]=find1(fa[x]);}void union1(int x,int y){x=find1(x);y=find1(y);if(y<x)swap(x,y);fa[y]=x;num[x]+=num[y];return ;}int main(){int t;scanf("%d",&t);while(t--){scanf("%d%d%d",&n,&m,&q);for(int i=0;i<m;i++)scanf("%d%d%d",&e[i].u,&e[i].v,&e[i].x);init();for(int i=0;i<q;i++){scanf("%d",&query[i].x);query[i].pos=i;}sort(e,e+m,cmp);sort(query,query+q,cmp1);int now=0;int tmp=0;for(int i=0;i<q;i++){//printf("now=%d\n",now);int j;for(j=now;j<m;j++){if(e[j].x>query[i].x)break;int fax=find1(e[j].u);int fay=find1(e[j].v);//printf("fax=%d fay=%d\n",fax,fay);if(fax==fay)continue;else{int tmp1=(num[fax]+num[fay])*(num[fax]+num[fay]-1);int tmp2=(num[fax]-1)*num[fax];int tmp3=(num[fay]-1)*num[fay];tmp+=(tmp1-tmp2-tmp3);union1(fax,fay);}}now=j;ans[query[i].pos]=tmp;}for(int i=0;i<q;i++)printf("%d\n",ans[i]);}return 0;}


0 0