Codeforces 1213G Path Queries

题目链接:https://www.luogu.org/problem/CF1213G

题意:给你一个有n个点的带权树,有m个查询,每次查询最大权值不大于 q 的简单路径数量。

分析:离线。先把所有边按照边长升序排序,再把所有询问按照w升序排序。

之后从小到大处理每个询问。对于一个询问,首先由于询问已经排好序了,所以前一个答案是之前加的边对于答案的贡献,我们就先把上一个询问的答案直接复制过来,之后把小于等于这个询问的w的所有边加入到树上,然后并查集更新答案:每加一条边,对答案产生的贡献是“这条边两端的连通块”大小之积。(因为是树,所以不会有两个点属于同一个联通块的数据出现)

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int inf=1e18;
const int maxn=2e5+7; 
const int mod=1e9+7;
#define mem0(a) memset(a,0,sizeof(a))
#define meminf(a) memset(a,0x3f,sizeof(a))
#define ls rt<<1
#define rs rt<<1|1
struct node{
    int u,v,w;
    bool operator <(const node &x)
    {
        return w<x.w;
    }
}e[maxn];
struct node2{
    int id,val;ll ans;
}q[maxn];
bool cmp1(const node2 &a,const node2 &b){
    return a.val<b.val;
}
bool cmp2(const node2 &a,const node2 &b){
    return a.id<b.id;
}
int par[maxn],siz[maxn];
int find(int x){
    if(x==par[x])return x;
    else return par[x]=find(par[x]);
}
void uni(int x,int y){
    x=find(x),y=find(y);
    if(x==y)return ;
    par[y]=x;
    siz[x]+=siz[y];
    siz[y]=siz[x];
}
int main(){
    int n,m;scanf("%d%d",&n,&m);
    for(int i=1;i<n;i++)scanf("%d%d%d",&e[i].u,&e[i].v,&e[i].w);
    for(int i=1;i<=m;i++){
        scanf("%d",&q[i].val);
        q[i].id=i;    
    }
    for(int i=1;i<=n;i++)par[i]=i,siz[i]=1;
    sort(e+1,e+n);
    sort(q+1,q+1+m,cmp1);
    for(int i=1,pos=1;i<=m;i++){
        q[i].ans=q[i-1].ans;
        while(pos<=n&&e[pos].w<=q[i].val)
        {
            int u=e[pos].u,v=e[pos].v;
            q[i].ans+=1ll*siz[find(u)]*siz[find(v)];
            uni(u,v);
            pos++;
        }
    }
    sort(q+1,q+1+m,cmp2);
    for(int i=1;i<m;i++){
        printf("%I64d ",q[i].ans);
    }
    printf("%I64d
",q[m].ans);
    return 0;
}
原文地址:https://www.cnblogs.com/qingjiuling/p/11624626.html