传送门biu~
设
si
s
i
表示前
i
i
个月份的收入和。每条信息相当于是在说
sr−sl−1=v
s
r
−
s
l
−
1
=
v
,可以用带权并查集来将
l−1
l
−
1
月份和
r
r
月份合并。设月份为
i
i
月份所在的集合中的根,那么维护一个来表示
i
i
点到根的距离,即。
①当把两个不在同一个集合里的元素
x
x
和用
v
v
合并时,设的根为
xn
x
n
,
y
y
的根为,则
disx=sx−sxn,disy=sy−syn,v=sy−sx
d
i
s
x
=
s
x
−
s
x
n
,
d
i
s
y
=
s
y
−
s
y
n
,
v
=
s
y
−
s
x
。如果用
xn
x
n
作为新集合的代表元素,那么
disyn=disx+v−disy
d
i
s
y
n
=
d
i
s
x
+
v
−
d
i
s
y
。
②假设
x
x
和已经在同一个集合中,距离为
v
v
,只需要验证是否等于
disx+v
d
i
s
x
+
v
,如果不相等则不成立。
#include<bits/stdc++.h>
using namespace std;
struct data{int l,r,num;}a[1005];
int father[105],dis[105];
int search(int x){
if(father[x]==x) return x;
int fa=search(father[x]);
dis[x]+=dis[father[x]];
return father[x]=fa;
}
int main(){
int T;
scanf("%d",&T);
while(T--){
int n,m;bool flag=false;
scanf("%d%d",&n,&m);
for(int i=0;i<=n;++i) father[i]=i,dis[i]=0;
for(int i=1;i<=m;++i) scanf("%d%d%d",&a[i].l,&a[i].r,&a[i].num);
for(int i=1;i<=m;++i){
int x=a[i].l-1,y=a[i].r,z=a[i].num;
int xn=search(x),yn=search(y);
if(xn^yn) dis[yn]=dis[x]+z-dis[y],father[yn]=xn;
else if(dis[y]!=dis[x]+z){flag=true;break;}
}
printf(flag?"false\n":"true\n");
}
return 0;
}