hdu5441 Travel

题目

Travel

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 m bidirectional 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

Source
2015 ACM/ICPC Asia Regional Changchun Online


思路:

长春站网络赛,昨天就是没做出来。
n个点m条边无向带权图,q个询问,对每次给定的限制x,不超过x相互可到达的顶点对数。
昨天的思路:
开始,存储边集数组,排序,存储所有的结果,二分查询结果就好。但是计算结果时犯了一个错误,每次加一条边,统计顶点数,记为n,我直接将结果赋 n(n1) ,没有考虑图不连通的情况,或者部分不连通的情况。比如郝哥哥后来扔给我的这组数据:
hdu5441_0

然后后来考虑使用并查集,小伙伴讲了个思路,首先判断是否连通。连通直接计算结果存储;不连通则让加边之前结果减两个集合结果相加,再加合并两个集合后结果。即:(变量名可以看下边代码)

    if(a==b){e[i].cx=e[i-1].cx;continue;}
    last=rankx[a]*(rankx[a]-1)+rankx[b]*(rankx[b]-1);
    uniont(e[i].u,e[i].v);
    a=findx(e[i].u);
    now=1LL*rankx[a]*(rankx[a]-1);
    if(i>0) e[i].cx=e[i-1].cx-last+now;
    else e[i].cx=now;

然后没时间了,下来写出来也没过。。。。。。
今天看有人使用离线带权并查集,只能说城会玩。还有人用了一个很神奇的公式,若连通,直接使用之前结果,不连通直接加 rank[a]rank[b]2 ,用这种方法过了,怎么推出来的,思考中。。。。。。。

对了,还有一点小意外。对i从0开始的下标,我操作的e[i-1].cx无编译错,测试结果正确,不过提交WA了,纠结半天找到它时,看着好纠结啊~~~


代码

#include<iostream>
#include<fstream>
#include<string>
#include<algorithm>
#include<math.h>
#include<stack>
#include<queue>
#include<set>
#include<map>
#include<vector>
#include<stdio.h>
#include<stdlib.h>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>

using namespace std;

typedef long long LL;
typedef long double real;
typedef vector<int> VI;

#define mes0(s)  memset(s,0,sizeof(s))
#define mes_1(s) memset(s,-1,sizeof(s))
#define mesINF(s) memset(s,INF,sizeof(s))

#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1


/**************************************************************
    Problem: hdu5441Travel
    Ordering: 图论,并查集
    Thought: 边集数组离线处理,二分结果
    Result: Accepted
    Author: wygdove
****************************************************************/


const double eps = 1e-9;
const double pi=acos(-1.0);
#define INF 0x3f3f3f3f
#define MINN -0x3f3f3f3f
#define MAXN 0x3f3f3f3f
#define MOD 10007
#define NUM 100008

struct Edge
{
    int u,v,w;
    LL cx;
}e[NUM];
int n,m,q;

bool cmp(Edge a,Edge b){return a.w<b.w;}

int parent[NUM],rankx[NUM];
void init()
{
    int i;
    for(i=1;i<NUM;i++)
    {
        parent[i]=i;
        rankx[i]=1;
    }
}
int findx(int x)
{
    if(x!=parent[x])
        parent[x]=findx(parent[x]);
    return parent[x];
}
void uniont(int x,int y)
{
    x=findx(x),y=findx(y);
    if(x!=y)
    {
        if(rankx[x]<rankx[y])
        {
            parent[x]=y;
            rankx[y]+=rankx[x];
        }
        else
        {
            parent[y]=x;
            rankx[x]+=rankx[y];
        }
    }
}


int findcx(int c)
{
    int left=1,right=m,mid;
    while(left<=right)
    {
        mid=(left+right)>>1;
        if(c<e[mid].w)right=mid-1;
        else left=mid+1;
    }
    if(c<e[mid].w)mid--;
    return mid;
}

int main()
{
    cin.sync_with_stdio(false);
    cout.sync_with_stdio(false);

    //freopen("hdu.in","r",stdin);
    //freopen("out.out","w",stdout);

    int T;
    int a,b,c;
    LL last,now;
    scanf("%d",&T);
    while(T--)
    {
        mes0(e);

        scanf("%d%d%d",&n,&m,&q);
        for(int i=1;i<=m;i++)
        {
            scanf("%d%d%d",&a,&b,&c);
            e[i].u=a;e[i].v=b;e[i].w=c;
        }

        sort(e+1,e+m+1,cmp);
        init();
        for(int i=1;i<=m;i++)
        {
            a=findx(e[i].u);b=findx(e[i].v);
            if(a==b) e[i].cx=e[i-1].cx;
            else
            {
                e[i].cx=e[i-1].cx+rankx[a]*rankx[b]*2;
                uniont(e[i].u,e[i].v);
            } 
        }
//for(int i=0;i<m;i++)printf("%d-%d : %d\t%d\n",e[i].u,e[i].v,e[i].w,e[i].cx);

        while(q--)
        {
            scanf("%d",&c);
            c=findcx(c);
            printf("%I64d\n",e[c].cx);
        }

    }

    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值