Codeforces Round #582 (Div. 3) G. Path Queries 题解(种类并查集+kruskal的思想)

题目链接

题目大意

给你一棵树,求有多少条简单路径(u,v),满足u到v这条路径上的最大值不超过k。q次查询。(u

题目思路

我还以为是换根dp啥的,没想到居然是类似于kruskal的思想来解题。

将所有边按权值从小到大排序,查询值qi也从小到大排序。对于每次查询的值qi,将边权小于等于qi的边通过并查集合并在一起。对于合并后的联通块,每个联通块对答案的贡献为size×(size−1)2,所有联通块的贡献直接求和即可。所以每次合并两个节点时,先将这两个节点所在的联通块的贡献减去,再加上合并后的贡献即可。

代码

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define fi first
#define se second
#define debug printf("I am here\n");
using namespace std;
typedef long long ll;
const int maxn=2e5+5,mod=2147493647,inf=0x3f3f3f3f;
int n,m,fa[maxn],num[maxn];
ll sum,ans[maxn];
pair<int,int> q[maxn];
struct node{
     
    int v,u,w;
}e[maxn];
int findd(int x){
     
    return x==fa[x]?fa[x]:fa[x]=findd(fa[x]);
    //最开始写成这样t了,注意还要使fa[x]=findd(fa[x])
    //return fa[x]==x?x:findd(fa[x]);
}
void unite(int a,int b){
     
    int faa=findd(a),fab=findd(b);
    if(faa==fab) return ;
    sum+=1ll*num[faa]*num[fab];
    num[faa]+=num[fab];
    fa[fab]=faa;
}
bool cmp(node a,node b){
     
    return a.w<b.w;
}
int main(){
     
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++){
     
        fa[i]=i;num[i]=1;
    }
    for(int i=1;i<=n-1;i++){
     
        scanf("%d%d%d",&e[i].v,&e[i].u,&e[i].w);
    }
    for(int i=1;i<=m;i++){
     
        scanf("%d",&q[i].fi);
        q[i].se=i;
    }
    sort(e+1,e+1+n-1,cmp);sort(q+1,q+1+m);
    int last=1;
    for(int i=1;i<=m;i++){
     
        for(int j=last;j<=n-1;j++){
     
            if(e[j].w<=q[i].fi){
     
                unite(e[j].v,e[j].u);
                last++;
            }else{
     
                break;
            }
        }
        ans[q[i].se]=sum;
    }
    for(int i=1;i<=m;i++){
     
        printf("%lld%c",ans[i],i==m?'\n':' ');
    }
    return 0;
}

参考链接:https://www.cnblogs.com/–Simon/p/11479662.html

你可能感兴趣的:(图论)