信友队提高二班 14 C

给出 \(n\) 个点 \(m\) 条边的无向图,点有颜色 \(c_i\),定义一个无序点对 \((i,j)\) 的权值为 \(i,j\) 所有路径中权值最大边的最小权值。求所有 \(|c_i-c_j|\ge L\) 的无序点对的权值。
\(n\le 2\times 10^5\),\(m\le 4\times 10^5\)。
首先有贡献的边一定是最小生成树(森林)上的边,因为任意加入一条非树边,形成的环上的边都不劣于它。
考虑在求解最小生成树(森林)的时候,每合并两个点集,两个点集间所有满足要求的点对的权值一定是合并这两个点集的边的权值 \(w\)。因为两个点集内部的边都是之前加入的,一定小于等于这条边。
因此,可以枚举一个点集中的点 \(u\),查询另一个点集中 \(\begin{cases}c_v\ge c_u+L\\c_v\le c_u-L\end{cases}\) 的点的个数 \(cnt\),对答案贡献 \(cnt\times w\)。这部分可以用平衡树维护点集的 \(c\) 值并查询排名。我选择使用 pb_ds。
具体地,采用启发式合并的思想,因为是无序点对,所以可以在合并时先枚举小点集中的点,在大点集中查询。然后把小点集的点合并进大的点集。
每一次合并,对于一个点,它所在集合大小至少 \(\times 2\),因此一个点最多被合并 \(\lfloor\log n\rfloor\) 次。每个点在合并时同时被枚举一次,因此至多查询 \(\lfloor\log n\rfloor\) 次。单次查询和合并一个点是 \(\mathcal{O}(\log n)\) 的。因此时间复杂度为 \(\mathcal{O}(n\log^2 n)\),空间复杂度为 \(\mathcal{O}(n+m)\)。
这里时间复杂度不是 \(\mathcal{O}(m\log^2n)\),因为总合并次数至多为 \(n-1\) 次。还要注意每次合并完要清空小点集的平衡树,否则空间复杂度会退化成 \(\mathcal{O}(n\log n)\)。
$\color{orange}点击查看代码$
#pragma GCC optimize("Ofast")
#include<bits/stdc++.h>
#include<ext/pb_ds/assoc_container.hpp>
#include<ext/pb_ds/tree_policy.hpp>
#define ll long long
#define G __gnu_pbds
#define pii pair<int,int>
#define fi first
#define se second
#define mp make_pair
using namespace std;
const int N=4e5+5;
int n,m,f[N],L,c[N];
ll ans;
G::tree<pii,G::null_type,less<pii>,G::rb_tree_tag,G::tree_order_statistics_node_update>t[N];
struct edge{
int u,v,w;
bool operator<(const edge&o)const{
return w<o.w;
}
}e[N];
int find(int x){
return f[x]==x?x:f[x]=find(f[x]);
}
void merge(int x,int y,int d){
int X=t[x].size(),Y=t[y].size();
if(X>Y){
f[y]=x;
for(auto it:t[y]){
ans+=1ll*d*(X-t[x].order_of_key(mp(it.fi+L,0)));
ans+=1ll*d*t[x].order_of_key(mp(it.fi-L+1,0));
}
for(auto it:t[y]){
t[x].insert(it);
}
t[y].clear();
}else{
f[x]=y;
for(auto it:t[x]){
ans+=1ll*d*(Y-t[y].order_of_key(mp(it.fi+L,0)));
ans+=1ll*d*t[y].order_of_key(mp(it.fi-L+1,0));
}
for(auto it:t[x]){
t[y].insert(it);
}
t[x].clear();
}
}
signed main(){
cin.tie(0);
cout.tie(0);
ios::sync_with_stdio(0);
cin>>n>>m>>L;
for(int i=1;i<=n;++i){
cin>>c[i];
f[i]=i;
t[i].insert(mp(c[i],i));
}
for(int i=1;i<=m;++i){
cin>>e[i].u>>e[i].v>>e[i].w;
}
stable_sort(e+1,e+m+1);
for(int i=1,uf,vf;i<=m;++i){
uf=find(e[i].u);
vf=find(e[i].v);
if(uf!=vf){
merge(uf,vf,e[i].w);
}
}
cout<<ans;
return 0;
}

浙公网安备 33010602011771号