ACM-ICPC 2015 Changchun Preliminary Contest E.Travel(排序+并查集)

链接:

        Travel

题意:

        T组样例,每组样例给定一个带边权的无向图,有q个询问,每次询问给定x,只保留权值<=x的边的话,输出有所有奇数个点的连通块中点对的数量和。

思路:

        根据边权大小对边排序,从小到大逐渐加入图中,遇到询问时进行查询。连通块大小通过并查集维护。

代码:

#include 
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
static const int maxn = 100010;
static const int INF = 0x3f3f3f3f;
static const int mod = (int)1e9 + 7;
static const double eps = 1e-6;
static const double pi = acos(-1);

void redirect(){
    #ifdef LOCAL
        freopen("test.txt","r",stdin);
    #endif
}

struct node{
    int x,y,z;
}p[maxn];

bool operator < (const node& a,const node& b){
    return a.z < b.z;
}

struct Query{
    int id,val;
}q[5050];

bool operator < (const Query& a,const Query& b){
    return a.val < b.val;
}

ll ans[5050];
int sz[maxn];
int fa[maxn];
inline int find(int x){
    if(fa[x] == x)return x;
    fa[x] = find(fa[x]);
    return fa[x];
}
inline void combine(int x,int y,ll& sum){
    int fx = find(x),fy = find(y);
    if(fx != fy){
        sum += (sz[fx]+sz[fy]) * (sz[fx]+sz[fy]-1) - sz[fx]*(sz[fx]-1) - sz[fy]*(sz[fy]-1);
        sz[fx] += sz[fy];
        fa[fy] = fx;
    }
}
int main(){
    redirect();
    int T,n,m,qu;
    scanf("%d",&T);
    while(T--){
        fill(sz,sz+maxn,1);
        for(int i = 1;i < maxn;i++)fa[i] = i;
        scanf("%d %d %d",&n,&m,&qu);
        for(int i = 1;i <= m;i++)scanf("%d %d %d",&p[i].x,&p[i].y,&p[i].z);
        sort(p+1,p+1+m);
        for(int i = 1;i <= qu;i++){
            q[i].id = i;
            scanf("%d",&q[i].val);
        }
        sort(q+1,q+1+qu);
        int j = 1;
        ll sum = 0;
        for(int i = 1;i <= qu;i++){
            while(j <= m && p[j].z <= q[i].val)combine(p[j].x,p[j].y,sum),j++;
            ans[q[i].id] = sum;
        }
        for(int i = 1;i <= qu;i++)printf("%lld\n",ans[i]);
    }
    return 0;
}

 

你可能感兴趣的:(并查集,ACM,并查集,排序)