HDU 5176 The Experience of Love 带权并查集
The Experience of Love
Time Limit: 4000/2000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others)
3 1 2 1 2 3 2 5 1 2 2 2 3 5 2 4 7 3 5 4
【Sample Output】
Case #1: 1 Case #2: 17
【Hint】
huge input,fast IO method is recommended.
In the first sample:
The maxValue is 1 and minValue is 1 when they select city 1 and city 2, the experience of love is 0.
The maxValue is 2 and minValue is 2 when they select city 2 and city 3, the experience of love is 0.
The maxValue is 2 and minValue is 1 when they select city 1 and city 3, the experience of love is 1.
so the sum of all experience is 1;
【题意】
给出一张图(其实是一棵树),要求计算图上任意两点间路径上最长边的总和以及任意两点间路径上最短边的总和,求其差。
【分析】
直白地考虑,枚举任意两点是O(n^2)的复杂度,然后还要找到两点的路径,然后还要记下路径上最长/最短边,然后求和,这样的复杂度是完全不可能承受的。
考虑一个子图被一条长度为c的边分成两部分,c的两端点为a和b,且这两部分中的所有边长都小于c。
则a所连接的点的数量*b所连接的点的数量就是这个子图中,任意两点直接路径上最长边为c的点对的总数,同理只要递增/递减地枚举每一条边,可以根据这个方法算出以每一条边为路径最长边的点对的总数,乘上边长求和即可。
需要解决的就是如何快速得到每条边两端的点数,带权并查集可以解决这个问题。
根据这个想法,可以首先对所有边进行排序,用并查集维护点对集关系,用权保存下每个点集中点的总数,用于每次得到一条边两端连接的点的个数。
最长边总和与最短边总和的思路完全一样,改一下代码方向即可。
【注意】
这里还有个坑是注意数据范围,要开到unsigned long long
/* *********************************************** MYID : Chen Fan LANG : G++ PROG : HDU5176 ************************************************ */ #include <iostream> #include <cstdio> #include <cstring> #include <algorithm> using namespace std; typedef struct nod { int a,b,c; } node; node a[150010]; bool op(node a,node b) { if (a.c==b.c) return a.a<b.a; else return a.c<b.c; } int father[150010],rank[150010]; void clean_father(int n) { for (int i=1;i<=n;i++) { father[i]=i; rank[i]=1; } } int getfather(int x) { if (father[x]!=x) father[x]=getfather(father[x]); return father[x]; } void link(int x,int y) { int xx=getfather(x),yy=getfather(y); father[xx]=yy; rank[yy]+=rank[xx]; } int main() { int n,t=0; while(scanf("%d",&n)==1) { for (int i=1;i<n;i++) scanf("%d%d%d",&a[i].a,&a[i].b,&a[i].c); sort(&a[1],&a[n],op); clean_father(n); unsigned long long ans1=0; for (int i=1;i<n;i++) { int x=getfather(a[i].a),y=getfather(a[i].b); ans1+=(unsigned long long)rank[x]*rank[y]*a[i].c; link(x,y); } clean_father(n); unsigned long long ans2=0; for (int i=n-1;i>=1;i--) { int x=getfather(a[i].a),y=getfather(a[i].b); ans2+=(unsigned long long)rank[x]*rank[y]*a[i].c; link(x,y); } t++; printf("Case #%d: %llu\n",t,ans1-ans2); } return 0; }