CodeForces - Path Queries (并查集+离线查询)

题目:https://vjudge.net/contest/323699#problem/A

题意:给你一棵树,然后有m个查询,每次查询问一条路径最大边小于给定查询的数量

思路:首先我们看到,我们其实可以计算出每个边权小于查询的所有连通块,然后sum+C(n,2),对每个连通块都加上值,然后就是答案了,但是这里注意查询数很多,我们肯定不能O(n)遍历每个查询,但是思路肯定是计算联通块里组合数的数量,怎么处理呢,我们注意到,他这个边权是的值的大小和我的连通块的有关,我们是否可以利用之前求出来的值呢,答案是可以的,我们可以对查询排序,先求值小的,然后到后面查询的时候用之前的连通块继续延伸,总的所有查询的复杂度我们也只是遍历了一遍树,但是我们dfs不好去处理,这里我们怎么弄呢,连通块的算法就那么几个,dfs,并查集,tarjan,我们可以排除dfs和tarjan,那么我们就肯定是用并查集了,我们对边权也进行排序,然后我们就可以利用每个边进行联通块的合并,然后贡献怎么计算呢,我们会发现A连通块和B连通块,然后A连通块的路径对已经算完了,B连通块的路径对算完了,然后现在要求的一个点在A连通块,一个点在Bl连通块,贡献就加上A.size*B.size

#include<bits/stdc++.h>
#define maxn 200005
#define mod 1000000007
using namespace std;
typedef long long ll;
struct sss{
    ll x,y,z;
}a[maxn];
struct eee{
    ll id,value;
}cx[maxn]; 
ll n,m,q;
long long  da[maxn];
ll siz[maxn];
ll f[maxn];
int cmp(struct sss x,struct sss y){
    return x.z<y.z;
} 
int cmp1(struct eee x,struct eee y){
    return x.value<y.value;
}
int find(int x){
    if(x==f[x]) return x;
    else return f[x]=find(f[x]);
}
int main(){
    scanf("%lld%lld",&n,&m);
    for(int i=0;i<n-1;i++){
        scanf("%lld%lld%lld",&a[i].x,&a[i].y,&a[i].z);
    }
    sort(a,a+n-1,cmp);
    for(int i=0;i<m;i++){
        cx[i].id=i;
        scanf("%lld",&cx[i].value);
    }
    sort(cx,cx+m,cmp1);
    for(int i=1;i<=n;i++){
        f[i]=i;
        siz[i]=1;
    }
    /*for(int i=0;i<n-1;i++){
        printf("%d %d %d\n",a[i].x,a[i].y,a[i].z);
    }*/ 
    ll dex=0;
    ll sum=0;
    for(int i=0;i<n-1;i++){
        if(a[i].z<=cx[dex].value){
            int xx=find(a[i].x);
            int yy=find(a[i].y);
            if(xx!=yy){
                sum+=siz[xx]*siz[yy];
                f[yy]=xx;
                siz[xx]+=siz[yy];
            }
        } 
        else{
            da[cx[dex].id]=sum;
            dex++;
            if(dex==m) break;
            i--;
        }
    }
    for(;dex<m;dex++){
        da[cx[dex].id]=sum;
    } 
    for(int i=0;i<m;i++){
        printf("%lld ",da[i]);
    }
} 

猜你喜欢

转载自www.cnblogs.com/Lis-/p/11469799.html
今日推荐