给定一个 个点, 条边的无向图,点有点权,边有边权,定义两个点之间的距离为两点间的所有路径中的边权的最大值的最小值。
现在问你,所有点权差不小于 的点对之间的距离和。
首先注意到,题目定义的距离为 两点间的所有路径中的边权的最大值的最小值 那么这就启发我们想到 kruskal 重构树。
有一个朴素想法,先建出重构树,然后枚举合法点对,求他们在树上的 LCA,时间复杂度为 。
考虑优化,注意到其实我们不需要枚举点对,直接计算每个重构树上的非叶子节点的贡献即可。
因为 kruskal 重构树是一个二叉树,所以只需要计算其中一个子树对另外一个子树的贡献,那么这就可以想到树上启发式合并。
具体做法就是先建出重构树,对于一个非叶子节点,我们先递归小子树,然后递归大子树,将两个子树内部的贡献算好,同时在我们递归大子树时,将其的叶子结点放入树状数组。然后再次递归小子树,这时候我们计算的是两个子树间有多少合法点对,这我们直接在树状数组上查找即可。
时间复杂度为 。
重构树的具体实现不再赘述,树上启发式合并再多嘴几句,我们在递归时打个标记表示这个子树要不要保留,假设这一整个子树都要保留那么我们将小子树的叶子结点也加入树状数组,否则我们删除大子树的所有叶子结点。
#include<bits/stdc++.h>
#define endl '\n'
#define int long long
using namespace std;
const int N = 2e6 + 10;
int n, m, d, tot, cnt;
int fa[N], c[N], a[N], tmp[N];
int ls[N], rs[N], l[N], r[N], siz[N];
struct Edge{
int x, y, w;
bool operator < (const Edge A){
return w < A.w;
}
}e[N];
struct BIT{
int t[N];
int lowbit(int x){
return x & (-x);
}
void update(int x, int val){
for(int i = x; i <= cnt; i += lowbit(i))
t[i] += val;
}
int query(int x){
int res = 0;
for(int i = x; i; i -= lowbit(i))
res += t[i];
return res;
}
}tr;
void lsh(){
sort(tmp + 1, tmp + 1 + n);
cnt = unique(tmp + 1, tmp + 1 + n) - tmp - 1;
for(int i = 1; i <= n; i++) c[i] = lower_bound(tmp + 1, tmp + 1 + cnt, c[i]) - tmp;
}
int find(int x){
if(x == fa[x]) return x;
return fa[x] = find(fa[x]);
}
void merge(int x, int y){
fa[y] = x;
siz[x] += siz[y];
}
void kruskal(){
tot = n;
for(int i = 1; i <= m; i++){
int x = e[i].x, y = e[i].y, w = e[i].w;
if(find(x) == find(y)) continue;
a[++tot] = w;
x = find(x), y = find(y);
if(siz[x] > siz[y]) ls[tot] = y, rs[tot] = x;
else ls[tot] = x, rs[tot] = y;
merge(tot, x), merge(tot, y);
}
}
int dfs1(int x){
int res = 0;
if(x <= n) return tr.query(cnt) - tr.query(r[c[x]] - 1 + (!d)) + tr.query(l[c[x]]);
res = dfs1(ls[x]) + dfs1(rs[x]);
return res;
}
void update(int x, int val){
if(x <= n){
tr.update(c[x], val);
return;
}
update(ls[x], val), update(rs[x], val);
}
__int128 ans;
void dfs(int x, bool del){
if(x <= n){
if(!del) tr.update(c[x], 1);
return;
}
dfs(ls[x], 1);
dfs(rs[x], 0);
ans += dfs1(ls[x]) * a[x];
if(del) update(rs[x], -1);
else update(ls[x], 1);
}
char ch[20];
int top;
void write() {
if(!ans) ch[++top] = 0;
while(ans != 0) ch[++top] = ans % 10, ans /= 10;
while(top) putchar(ch[top--] + '0');
putchar('\n');
return;
}
signed main(){
freopen("novel.in", "r", stdin);
freopen("novel.out", "w", stdout);
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
cin >> n >> m >> d;
for(int i = 1; i <= n; i++) cin >> c[i], tmp[i] = c[i];
lsh();
for(int lst = 0, i = 1; i <= cnt; i++){
while(lst + 1 <= i && tmp[i] - tmp[lst + 1] >= d) lst++;
l[i] = lst;
}
for(int lst = cnt + 1, i = cnt; i >= 1; i--){
while(lst - 1 >= i && tmp[lst - 1] - tmp[i] >= d) lst--;
r[i] = lst;
}
for(int i = 1; i <= 2 * n; i++) fa[i] = i, siz[i] = 1;
for(int i = 1; i <= m; i++)
cin >> e[i].x >> e[i].y >> e[i].w;
sort(e + 1, e + 1 + m);
kruskal();
dfs(find(1), 1);
write();
return 0;
}
__EOF__
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现