旅行

24 篇文章 0 订阅
9 篇文章 0 订阅

旅行

Time Limit: 1 Sec Memory Limit: 128 MB

Description

A国有n座城市,每座城市都十分美,这使得A国的民众们非常喜欢旅行。然而,A国的交通十分落后,这里只有m条双向的道路,并且这些道路都十分崎岖,有的甚至还是山路,只能靠步行。通过每条道路的长度、泥泞程度等因素,我们给每条道路评估一个“崎岖度”,表示通过这条道路的不舒适程度。
从X城市经过若干条道路到达Y城市,我们称这次旅行的“代价”为所经过道路“崎岖度”的最大值。当然,如果从X城市到Y城市有多条路线,民众们会自觉选择“代价”最小的路线进行旅行。但是,A国的民众也是有脾气的,如果旅行的“代价”超过了他们的“忍耐度”,他们就不选择这个旅行了,甚至宁愿在家里宅着。
现在A国的国王想进行若干次询问:给定民众的“忍耐度”,问还有多少对城市(X,Y)会存在旅行?请你对国王的每次询问分别给出回答。

Input

第1行三个正整数n、m、Q,分别表示城市数量、道路数量和询问次数。
第2行到第m+1行每行三个正整数x、y、w,表示x号城市和y号城市之间有一条“崎岖度”为w的双向道路。
第m+2行至第m+Q+1行,每行一个正整数k,表示询问中给定的“忍耐度”为k。

Output

共Q行,对于每次询问做出回答。

Sample Input

5 5 2
1 2 1
2 3 2
3 4 1
4 5 4
5 1 1
1
2

Sample Output

4
10

HINT

【样例说明】

第一个询问:(1,2)、(1,5)、(2,5)、(3,4)。其中(2,5)的具体走法为:2-1-5
第二个询问:(1,2)、(1,3)、(1,4)、(1,5)、(2,3)、(2,4)、(2,5)、(3,4)、(3,5)、(4,5)。其中(4,5)的具体走法为:4-3-2-1-5

【数据规模】

对于20%的数据满足n<=20,m<=40,Q<=40;
对于40%的数据满足n<=1000,m<=2000,Q<=1000;
对于60%的数据满足n<=3000,m<=6000,Q<=200000;
对于100%的数据满足n<=100000,m<=200000,Q<=200000。其他数不超过10^9。

【细节提示】

1 给出的n个城市不一定全部互相连通,且两个城市之间可能存在多条道路,也可能存在某条边是从某城市出发回到他自己。
2 对于询问的结果可能很大,请注意使用适当的类型存储。


并查集+二分查找
Aim:求有多少条路径,其最大值<=k,即存在多少(x,y)点对。
But!!这样会十分麻烦,比如说我第一次看到这道题就想到了NOIP2013 D1 T3的货车运输:在线搞一下LCA,然后n^2枚举所有点对,如果满足就累加。不过这样不超时才怪。。而且有种预感,肯定有更巧妙的方法。
所以,我们不妨转换一下思路,题意:若边权<=K才能联通,则现在有多少对城市能够联通 这样看起来就清晰了许多。
而且数据很大,题中也提示了需要选择适当的类型存储。这样很自然的想到可以用并查集来搞一下:

  • 维护连通性
  • 维护路径数
  • 维护边联通点数

下面介绍一下解决本题所需要用到的数组即他们的意义:

  1. fa[i]表示i的父节点
  2. s[i]表示以i为根联通点的个数
  3. f[i]表示第i条边联通点的对数

举几个例子来理解一下这些数组的含义:

对于x,y来说,很显然y是x的父节点,同时s[x]=3;s[y]=3;
在利用并查集维护的时候在合并父节点的同时可以直接合并一下s[],对于上面的例子显然可以s[y]+=s[x];
但是f[i]并不好理解,先说一下转移:f[i]=之前联通的点对数+新联通的点对数
=>f[i-1]+s[t1]*s[t2]
( 对于每条边t1,t2分别表示这条边起点和终点的父节点 )
s[t1]*s[t2]也非常容易理解,即s[t1]中的所有点都能与s[t2]中的点相连通。
So,最后,我们需要找了。
为了查找方便,我们需要在进行上述操作前先按边权值升序排序,而上述操作实际上是在构造最小生成树。为了加速查找需要用到二分查找,姿势神马的就不用多强调了。最后找出Val<=k的最后一个边,此边联通的点对数就是我们需要的答案。


Code:

#include <stdio.h>
#include <string.h>
#include <algorithm>
#define MAXN 205000
using namespace std;
struct node
{
    int from;
    int to;
    int val;
    int next;
}edge[MAXN<<1];
int n,m,q,k,cnt;
int head[MAXN],fa[MAXN],s[MAXN],f[MAXN];
int cmp(node a,node b){return a.val < b.val ? 1 : 0;}
void init()
{
    memset(head,-1,sizeof(head));
    cnt=1;
    for(int i=1;i<=n;i++)
    {
        fa[i]=i;
        s[i]=1;
    }
}
void addedge(int from,int to,int val)
{
    edge[cnt].from=from;
    edge[cnt].to=to;
    edge[cnt].val=val;
    edge[cnt].next=head[from];
    head[from]=cnt++;
}
int find(int v)
{
    if(fa[v]!=v) 
    {
        fa[v]=find(fa[v]);
    }
    s[v]=s[fa[v]];
    return fa[v];
}
int search(int x)
{
    int l=1,r=m,mid,ret;
    while(l<=r)
    {
        mid=(l+r)>>1;
        if(edge[mid].val<=x)
        {
            l=mid+1;
            ret=mid;
        }
        else r=mid-1;
    }   
    return ret;
}
void print()
{
    while(q--)
    {
        scanf("%d",&k);
        printf("%d\n",f[search(k)]);
    }
}
int main()
{
    scanf("%d%d%d",&n,&m,&q);
    init();
    for(int i=1;i<=m;i++)
    {
        int a,b,c;
        scanf("%d%d%d",&a,&b,&c);
        addedge(a,b,c);
    }
    sort(edge+1,edge+1+cnt,cmp);
    for(int i=1;i<=cnt;i++)
    {
        int t1=find(edge[i].from);
        int t2=find(edge[i].to);
        f[i]=f[i-1];
        if(t1!=t2)
        {
            f[i]+=s[t1]*s[t2];
            fa[t1]=t2;
            s[t2]+=s[t1];   
        }
    }
    print();
    return 0;
}

/*

时间复杂度为O[m*(log m)+q];

*/ 
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值