题目链接: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; }