CodeForces - 1213 G.Path Queries (并查集)

题意:

给一棵n个顶点的树,m个询问,
每个询问给k,图中有多少对点对u,v(u<v)
满足u,v路径上的最长边不大于k

分析:

结构体存储所有边和所有询问
对边按权值从小到大排序,对询问按k从小到大排序
遍历所有询问
对于每个询问中的k,将所有边权小于等于k的边的端点用并查集合并成同一连通块
此时每个 连通块中任意点对间的最长边都小于等于k,

每次合并两个连通块,假设两个连通块中的点数为x和y,则点对数量增加xy,答案累加xy
(x所在连通块的每个点 连上了y中的每个点,总增加路径为xy)

也可以写成
答案减去x(x-1)/2减去y(y-1)/2加上(x+y)(x+y-1)/2;
减去旧的连通块中的总路径数量,计算新的连通块中的总路径数量

ps:
居然原题。
hdu3938,hdu5441

code:
#include<cstdio>
#include<cstring>
#include<iostream>
#include<cmath>
#include<algorithm>
const int inf=0x3f3f3f3f;
const int inn=0x80808080;
using namespace std;
#define int long long//necessary
const int maxm=2e5+5;
struct Edge{
    int a,b,c;
    bool operator<(Edge &a){
        return c<a.c;
    }
}e[maxm];
struct Query{
    int k,id;
    bool operator<(Query &a){
        return k<a.k;
    }
}q[maxm];
int pre[maxm];
int num[maxm];
int res[maxm];
void init(){
    for(int i=0;i<maxm;i++){
        pre[i]=i;
        num[i]=1;
    }
}
int ffind(int x){
    return pre[x]==x?x:pre[x]=ffind(pre[x]);
}
signed main(){
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    init();
    int n,m;
    cin>>n>>m;
    for(int i=0;i<n-1;i++){
        cin>>e[i].a>>e[i].b>>e[i].c;
    }
    sort(e,e+n-1);
    for(int i=0;i<m;i++){
        cin>>q[i].k;
        q[i].id=i;
    }
    sort(q,q+m);
    int ans=0;
    int now=0;
    for(int i=0;i<m;i++){
        while(now<n-1&&e[now].c<=q[i].k){
            int x=ffind(e[now].a);
            int y=ffind(e[now].b);
            if(x!=y){
                ans+=num[x]*num[y];//路径增加数量(即点对增加数量)
                /*
                    或者写成:
                    ans-=num[x]*(num[x]-1)/2;//减去旧的连通块中的总路径数量
                    ans-=num[y]*(num[y]-1)/2;//减去旧的连通块总路径数量
                    ans+=(num[x]+num[y])*(num[x]+num[y]-1)/2;//计算新的连通块中的总路径数量
                */
                pre[y]=x;//合并
                num[x]+=num[y];//合并
            }
            now++;
        }
        res[q[i].id]=ans;
    }
    for(int i=0;i<m;i++){
        cout<<res[i]<<' ';
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值