您的位置:首页 > 其它

hdoj 5441 Travel 【在边权限制下 并查集划分、合并连通块,求解点对数目】

2015-09-14 19:16 417 查看

Travel

Time Limit: 1500/1000 MS (Java/Others) Memory Limit: 131072/131072 K (Java/Others)

Total Submission(s): 613 Accepted Submission(s): 248



Problem Description

Jack likes to travel around the world, but he doesn’t like to wait. Now, he is traveling in the Undirected Kingdom. There are n cities
and m bidirectional
roads connecting the cities. Jack hates waiting too long on the bus, but he can rest at every city. Jack can only stand staying on the bus for a limited time and will go berserk after that. Assuming you know the time it takes to go from one city to another
and that the time Jack can stand staying on a bus is x minutes,
how many pairs of city (a,b) are
there that Jack can travel from city a to b without
going berserk?


Input

The first line contains one integer T,T≤5,
which represents the number of test case.

For each test case, the first line consists of three integers n,m and q where n≤20000,m≤100000,q≤5000.
The Undirected Kingdom has n cities
and mbidirectional
roads, and there are q queries.

Each of the following m lines
consists of three integers a,b and d where a,b∈{1,...,n} and d≤100000.
It takes Jack d minutes
to travel from city a to
city b and
vice versa.

Then q lines
follow. Each of them is a query consisting of an integer x where x is
the time limit before Jack goes berserk.



Output

You should print q lines
for each test case. Each of them contains one integer as the number of pair of cities (a,b) which
Jack may travel from a to b within
the time limit x.

Note that (a,b) and (b,a) are
counted as different pairs and a and b must
be different cities.


Sample Input

1
5 5 3
2 3 6334
1 5 15724
3 5 5705
4 3 12382
1 3 21726
6000
10000
13000




Sample Output

2
6
12




定义<a, b>为a到b 路径上权值最大的边的权值。
题意:给你一个N个点和M条边的无向图,每条边都有一个权值。现在有Q次查询,每次一个值limit,问你满足<a, b> <= limit的 点对有多少个?
注意<a,b> 和 <b,a>是不同的点对。

用node[u]表示节点u所在连通块里面的节点数目。用ans统计满足当前limit值的前提下,满足的点对数(这里和下面的点对数全是答案的一半)。
思路:用并查集划分、合并连通块并记录每个连通块里面的节点数目。
1,连通所有边权小于limit的边,显然这时得到的连通块里面的点对数目就是我们要的答案。
2,对于a所在的连通块和b所在连通块,合并后增加的点对数有node[a] * node[b],因此我们只需要累计增加的点对数ans,最后的答案就是ans * 2。

首先我们先对所有边按照边权升序排列。这样对当前limit值,连通边的终止条件就是edge[i].val > limit (0 <= i < M)。

先看个TLE代码:

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
#define MAXN 20000+10
#define MAXM 100000+10
#define INF 0x3f3f3f3f
using namespace std;
int N, M, Q;
struct Edge
{
    int from, to, val;
};
Edge edge[MAXM];
bool cmp(Edge a, Edge b)
{
    return a.val < b.val;
}
void getMap()
{
    scanf("%d%d%d", &N, &M, &Q);
    for(int i = 0; i < M; i++)
        scanf("%d%d%d", &edge[i].from, &edge[i].to, &edge[i].val);
    sort(edge, edge+M, cmp);
}
struct rec
{
    int limit, index;
};
rec q[MAXN];
bool cmp1(rec a, rec b)
{
    return a.limit < b.limit;
}
int pre[MAXN], node[MAXN];//父节点 和 连通块的节点数目
void init()
{
    for(int i = 1; i <= N; i++)
        pre[i] = i, node[i] = 1;
}
int Find(int p)
{
    int t;
    int child = p;
    while(p != pre[p])
        p = pre[p];
    while(child != p)
    {
        t = pre[child];
        pre[child] = p;
        child = t;
    }
    return p;
}
void solve()
{
    int limit;
    for(int i = 0; i < Q; i++)
    {
        scanf("%d", &limit);
        init();
        int ans = 0;
        for(int j = 0; j <= M && edge[j].val <= limit; j++)
        {
            int u = Find(edge[j].from);
            int v = Find(edge[j].to);
            if(u != v)
            {
                ans += 2 * (node[u] * node[v]);
                pre[u] = v;
                node[v] += node[u];
            }
        }
        printf("%d\n", ans);
    }
}
int main()
{
    int t;
    scanf("%d", &t);
    while(t--)
    {
        getMap();
        solve();
    }
    return 0;
}


因为求解每一次查询,都要遍历一次所有边,为了优化时间复杂度,我们可以先按查询的limit值排序,并记录当前查询的顺序index。
若q[i].limit > q[j].limit,我们只需要在q[j].limit的基础上继续连通新的边p(edge[p].val <= q[i].limit),求出新增加的点对数。在不能连通新边的时候跳出,并记录当前查询q[i].index的结果。详看代码

AC代码:

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
#define MAXN 20000+10
#define MAXM 100000+10
#define INF 0x3f3f3f3f
using namespace std;
int N, M, Q;
struct Edge
{
    int from, to, val;
};
Edge edge[MAXM];
bool cmp(Edge a, Edge b)
{
    return a.val < b.val;
}
void getMap()
{
    scanf("%d%d%d", &N, &M, &Q);
    for(int i = 0; i < M; i++)
        scanf("%d%d%d", &edge[i].from, &edge[i].to, &edge[i].val);
    sort(edge, edge+M, cmp);
}
struct rec
{
    int limit, index;
};
rec q[MAXN];
bool cmp1(rec a, rec b)
{
    return a.limit < b.limit;
}
int pre[MAXN], node[MAXN];//父节点 和 连通块的节点数目
void init()
{
    for(int i = 1; i <= N; i++)
        pre[i] = i, node[i] = 1;
}
int Find(int p)
{
    int t;
    int child = p;
    while(p != pre[p])
        p = pre[p];
    while(child != p)
    {
        t = pre[child];
        pre[child] = p;
        child = t;
    }
    return p;
}
int sum[MAXN];//记录查询结果
void solve()
{
    for(int i = 0; i < Q; i++)
        scanf("%d", &q[i].limit), q[i].index = i;
    sort(q, q+Q, cmp1);//排序后优化时间复杂度
    init();
    int j = 0;
    int ans = 0;
    for(int i = 0; i < Q; i++)
    {
        for(; j <= M && edge[j].val <= q[i].limit; j++)
        {
            int u = Find(edge[j].from);
            int v = Find(edge[j].to);
            if(u != v)
            {
                ans += 2 * (node[u] * node[v]);
                pre[u] = v;
                node[v] += node[u];
            }
        }
        sum[q[i].index] = ans;//记录结果
    }
    for(int i = 0; i < Q; i++)
        printf("%d\n", sum[i]);
}
int main()
{
    int t;
    scanf("%d", &t);
    while(t--)
    {
        getMap();
        solve();
    }
    return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: