题目链接 : https://vjudge.net/contest/320541#problem/C
Sample Input
10 5
1 10 100
7 10 28
1 3 32
4 6 41
6 6 1
Sample Output
1
翻译:n(n=10)个数组成的序列,(不知道这个序列的内容),q(q=5)次询问,每次三个数,表示 [s,t]区间的和。随着输入,若出现与上面的区间和矛盾,就记录矛盾的次数。
比如[1,10]和为100,[1,4]的和为40,[5,10]的和为30,这三组数据中肯定有一组数据是错误的。
分析:
先对输入区间的处理,把[u,v]变成(u-1,v],把区间连在一起。
开一个数组sum,记录某点到其祖先节点的距离。对于每一个范围,用并查集把端点合并到一个集合。对于接下来的范围,如果端点在同一集合,可以直接用两点到祖先节点的差值与给点的区间长度比较,不在同一集合,合并,找出根节点与节点的距离关系。
从上图我们可以看出,当roota!=rootb时 如果将roota并入rootb,那么是不是
roota->rootb = b->rootb - b->roota
我们可以知道 b->roota = a->roota - a->b
最后可以推出 roota ->rootb = b->rootb + a->b - a->roota
因为,sum[roota]= roota->rootb
推出得到 sum[roota] = -sum[a]+sum[b]+v
以下面这组数据模拟过程:
10 3
1 10 100
1 4 40
5 10 60
0
10 3
1 10 100
roota–>0,rootb—>10,father[0]—>10
(0,10的根节点不同,把0的祖先看成是10)
sum[0]—>100(0到祖先10的距离为100)
1 4 40
roota–>10,rootb—>4,father[10]—>4
(0,4的根节点不同,把10的祖先看成是4)
sum[10]—>-60(10到祖先的距离为-60)
5 10 60
roota–>4,rootb—>4(祖先相同可以直接比较)
sum[5]–>0,sum[10]–>-60
0
代码:
#include<stdio.h>
#include<algorithm>
#include<string.h>
using namespace std;
#define N 200010
int father[N];
int sum[N]; ///记录当前结点到根结点的距离
int getf(int x)
{
if(x!=father[x])
{
int t=father[x];
father[x]=getf(father[x]);
sum[x]+=sum[t];
}
return father[x];
}
int main()
{
int n,m;
while(~scanf("%d%d",&n,&m))
{
for(int i=0; i<=n; i++)
{
father[i] = i;
sum[i] = 0;
}
int ans = 0;
while(m--)
{
int a,b,v;
scanf("%d%d%d",&a,&b,&v);
a--;
int roota = getf(a);
int rootb = getf(b);
if(roota==rootb)
{
//printf("sum[a]-->%d,sum[b]-->%d\n",sum[a],sum[b]);
if(sum[a]-sum[b]!=v)
ans++;
}
else
{
father[roota] = rootb;
//printf("roota-->%d,rootb--->%d,father[%d]--->%d\n",roota,rootb,roota,father[roota]);
sum[roota] = -sum[a]+sum[b]+v;
//printf("sum[%d]--->%d\n",roota,sum[roota]);
}
}
printf("%d\n",ans);
}
return 0;
}