题目地址:http://acm.hdu.edu.cn/showproblem.php?pid=5441
思路:离线处理。将边权值按从小到大排序,查询标号后按照从小到大排序。对于每次查询,依次将比当前查询值小的边加入并查集。对于两个符合条件即将合并的连通块增加答案个数num[x]*num[y]*2 。合并:fa[x]=y; num[y]+=num[x]; 。最后依次输出结果即可。
#include<cstdio> #include<cstring> #include<iostream> #include<algorithm> #define debu using namespace std; const int maxn=2e4+50; const int maxm=1e5+50; struct Node { int u,v,w,id; }; int n,m,q; Node e[maxm],cmd[5005]; int ans[5005],fa[maxn],num[maxn]; int cmp(Node a,Node b) { return a.w<b.w; } int Find(int x) { return x==fa[x]?x:fa[x]=Find(fa[x]); } int main() { #ifdef debug freopen("in.in","r",stdin); #endif // debug 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].w); for(int i=0; i<q; i++) { scanf("%d",&cmd[i].w); cmd[i].id=i; } sort(e,e+m,cmp); sort(cmd,cmd+q,cmp); memset(ans,0,sizeof(ans)); for(int i=1; i<=n; i++) { fa[i]=i; num[i]=1; } int pre=0,j,tot=0; for(int i=0; i<q; i++) { int tmp=cmd[i].id; for(j=pre; e[j].w<=cmd[i].w&&j<m; j++) { int x=Find(e[j].u); int y=Find(e[j].v); if(x!=y) { fa[x]=y; tot+=num[x]*num[y]*2; num[y]+=num[x]; } } pre=j; ans[tmp]=tot; } for(int i=0; i<q; i++) printf("%d\n",ans[i]); } return 0; }
原文:http://blog.csdn.net/wang2147483647/article/details/52122727