[codevs1450] xth的旅行

题目描述 Description
毕业了,Xth很高兴,因为他要和他的 rabbit 去双人旅行了。他们来到了水城威尼
斯。众所周知(⊙﹏⊙b汗),这里的水路交通很发达,所以 xth 和 rabbit 只好坐
船穿梭于各个景点之间。但是要知道,rabbit 是会晕船的,看到她难受,xth 是会
心疼的。
已知城市中有n个景点,这些景点之间有m条双向水路,在每条水路上航行时
rabbit 都会有一个“晕船值”。旅行时,xth 会带着 rabbit 尽量选择晕船值小的路线
旅行。但是 rabbit 也是有一定忍耐限度度的,如果晕船值超过了她的忍耐度,xth
会果断决定放弃这条路线。
现在 xth 想进行若干次询问,给定 rabbit 的忍耐度,问还有多少对城市(x,y)间会存
在可行的旅行路线(如果(x,z)和(z, y)可行,则(x,y)可行,也就是说连通性是可传
递的)。

输入描述 Input Description
第 1 行三个正整数n、m、q,分别表示景点数量、水路数量和询问次数。
第 2 行到第m + 1行每行三个正整数x、y、w,表示x号景点和y号景点之间有一条
“晕船值”为w的双向水路。
第m + 2行至第m + q + 1行,每行一个正整数k,表示询问中给定的 rabbi忍耐度
为k。

输出描述 Output Description
共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

数据范围及提示 Data Size & 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。

询问有限制的题,先想离线
把所有询问从小到大排序,再跑最小生成树
保证当前生成树中的所有边小于目前询问
如果发现要添的边大了,更新目前询问的答案,处理下一组询问;

更新答案:对于新合并的两棵树A和B,对答案贡献为A的节点数*B的节点数

树的节点数在并查集合并时记录,因为每棵树标志元素是不变的,合并时标志元素的cnt加一下就好了

本来以为需要打高精……然而2000000^2好像longlong就能过

#include<iostream>
#include<cstdio>
#include<algorithm>
#define LL long long
using namespace std;
const int MAXN = 200000 + 50;
LL n,m,q;
struct edge
{
    LL f,t,v;
}l[MAXN << 1];
bool cmp(edge a,edge b)
{
    return a.v < b.v;
}
struct zt
{
    LL num,que,ans;
}Q[MAXN];
LL a,b,c;
LL ans = 0,cnt[MAXN],fa[MAXN],rank[MAXN];
void init(LL n)
{
    for(LL i = 1;i <= n;i ++)
        fa[i] = i,cnt[i] = 1;
}
LL find(LL x)
{
    if(fa[x] == x)return x;
    LL temp = find(fa[x]);
    return fa[x] = temp;
}
void merge(LL a,LL b)
{
    a = find(a);b = find(b);
    LL temp = cnt[a] + cnt[b];
    cnt[a] = cnt[b] = temp;
    if(rank[a] < rank[b])swap(a,b);
    fa[b] = a;
    if(rank[a] == rank[b])rank[a] ++;
}
bool same(LL a,LL b)
{
    return find(a) == find(b);
}
bool cmp1(zt a,zt b)
{
    return a.que < b.que;
}
bool cmp2(zt a,zt b)
{
    return a.num < b.num;
}
int main()
{
    scanf("%lld%lld%lld",&n,&m,&q);
    init(n);
    for(LL i = 1;i <= m;i ++)
    {
        scanf("%lld%lld%lld",&a,&b,&c);
        l[i] = (edge){a,b,c};
    }
    sort(l + 1,l + m + 1,cmp);
    for(LL i = 1;i <= q;i ++)
    {
        scanf("%lld",&Q[i].que);
        Q[i].num = i;
    }
    sort(Q + 1,Q + q + 1,cmp1);
    LL head = 1;
    for(LL i = 1;i <= m;i ++)
    {
        LL f = l[i].f,t = l[i].t;
        while(l[i].v > Q[head].que)
        {
            Q[head].ans = ans;
            head ++;
        }
        if(!same(f,t))
        {
            f = find(f);
            t = find(t);
            ans += cnt[f] * cnt[t];
            merge(f,t);
        }
    }
    while(head <= q)
    {
        Q[head ++].ans = ans;
    }
    sort(Q + 1,Q + q + 1,cmp2);
    for(int i = 1;i <= q;i ++)
    {
        printf("%lld\n",Q[i].ans);
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值