原题链接: vjudge传送门
题目大意:
有N个数,不知道它们具体的值,但是知道某两个数之间(包括这两个数)的所有数之和,
给出M个区间即区间和,需要判断有多少个区间和与前边已知的区间和存在矛盾。
例如给出区间和[1,5]为30,[3,5]为15,再给出[1,3]为20,显然[1,3]的值应该为30-15=15,故该答案错误,需求解错误答案个数
(需把闭区间看成半开半闭区间理解)
具体思路:
带权并查集,我也是第一次接触,看了题解也看了好半天
给定一个区间,若区间两端点是同一集合,判断是否区间和是否与已知矛盾,若不矛盾则把两个端点加入同一集合
若两个端点不是同一集合,直接合并集合。
DaLao的题解https://cloud.tencent.com/developer/article/1073376 ,Orz
令sum[x]表示区间[x,f[x]]的和,这个可以在路径压缩的时候更新,
对于一组数据(u,v,w),令r1=Find(u),r2=Find(v),
于是若r1= =r2,此时u,v就有了相同的参考点,而sum[u]为区间[u,r1(r2)]的和,sum[v]为区间[v,r2(r1)]的和,
于是只需判断w= =sum[v]-sum[u]即可;
若r1
对于情况1来说,此时father[r1]=r2,sum[r1]=sum[v]-(sum[u]-w);
根据坐标轴(u,v,r1,r2)就可以理解:sum[r1]=sum[v]-(sum[u]-w)
对于情况2有sum[r1]=w-sun[u]+sum[v].
通过观察,我们可以发现情况1和情况2的结果是一样的,于是可以合并。
同理,对于r1>r2这种情况也一样,这里就不在赘述了。
具体代码:
#include
#include
#include
#include
using namespace std;
int const MAXN = 2e5 + 10;
int sum[MAXN], fa[MAXN]; //sum[i]代表从第i位加到第fa[i]位的和
int n, m;
void init() {
memset(sum, 0, sizeof(sum));
for (int i = 0; i <= n; i++)
fa[i] = i;
}
int find(int x)
{
if (x == fa[x])
return x;
int tmp = fa[x]; //路径压缩时顺便更新sum数组
fa[x] = find(fa[x]);
sum[x] += sum[tmp];
return fa[x];
}
bool Union(int u, int v, int w)
{
int r1 = find(u);
int r2 = find(v);
if (r1 == r2) {
if (sum[u] == w + sum[v])
return true;
return false;
}
else {
//不在同一集合
fa[r1] = r2; //合并集合
sum[r1] = sum[v] - sum[u] + w; //更新sum数组
return true;
}
}
int main()
{
int u, v, w, ans;
while (~scanf("%d%d", &n, &m))
{
init();
ans = 0;
while (m--)
{
scanf("%d%d%d", &u, &v, &w);
if (!Union(u - 1, v, w))ans++; //注意此处u要减1
}
printf("%d\n", ans);
}
return 0;
}