输入
10 5
1 10 100
7 10 28
1 3 32
4 6 41
6 6 1
解释: 10个数字, 5次询问;
1 10 100 意思是1到10 的和为100
以此类推
输出
1
解释: 有一句话是错误的,
想法
1 10 100 可以看作10比1 大100 所以1要减一
如果想合并a, b,最终目的是求a,b,到其共同基准的距离.首先求出a, b的祖宗,即比较标准x, y,默认x认y为爹,(a的祖先认b的祖先为爹)所以a的祖先变成了y, x的祖先变成了y,所以a到a的祖先的距离(a到y)=(a到x) + (x 到y), b到y的距离即为sum[b]没变;
下面推倒,如图
1:sum[a] = v + k;
2:sum[b] = k + (y-x);
2-1: y-x = sum[b] - sum[a] + v;
从而另sum[x] = sum[b] - sum[a] + v; 而后更新sum[a].更新的部分放在了查找中.
这里转自https://blog.csdn.net/dextrad_ihacker/article/details/51016017
主要是带权的理解。
并查集专题讲解 边带权+扩展域
这个视频里面有这个题的关键部分的理解内容。
下面代码
#include<iostream>
#include<cstdio>
using namespace std;
const int maxn = 2e5+5;
int f[maxn];
int num[maxn];
void init(int m)
{
for(int i = 0; i <= m; i ++)
{
f[i] = i;
num[i] = 0;
}
}
int find(int x)
{
if(f[x] == x)
return x;
else
{
int q = find(f[x]);
num[x] += num[f[x]];
f[x] = q;
return f[x];
}
}
int main()
{
int n, m;
while(~scanf("%d%d",&n,&m))
{
init(n);
int ans = 0;
for(int i = 1; i <= m; i ++)
{
int a, b, s;
scanf("%d%d%d",&a,&b,&s);
a --;//为了计算a到b的距离
int a1 = find(a);
int b1 = find(b);
if(a1 == b1)
{
if(num[b] - num[a] != s)
ans ++;
}
else
{
f[b1] = a1;
num[b1] = s + num[a] - num[b];
//主要是这里的理解,就在视频里面
}
}
printf("%d\n",ans);
}
return 0;
}