input 改变为开区间后的input
10 5
1 10 100 0 10 100
7 10 28 6 10 28
1 3 32 0 3 32
4 6 41 3 6 41
6 6 1 5 6 1
output
1
文字版在博客中显示格式不太对,对图片版不满意的可以复制到DEV C++中变注释观看,注释颜色可任意调整
思路:变闭区间为开区间,方便连通使得并查集合并,输入时,先判断他们根节点fl与fr是否相同,value在本题中为节点到根节点的偏移量
是则说明两元素同属一个并查集,判断给出的偏移量与算出的偏移量是否相等,不相等则ans++,方程为value[l]==value[r]+v ,算法见推导
否则考虑合并,把fl的父节点赋值为fr,因为并查集的合并操作核心是对根节点的操作,fl的权值value[fl]=v+value[r]-value[l]
权值和相等算法推导
三角型关系时,例如下面案例:l=0,r=3,v=32,fl=10,fr=3 (第三行输入)
0———> 10 0->10这条边权值为value[0]=100(value是节点到边的偏移量,0到10的偏移量就是边权)
| | 0->3的边权是题中给出的v=32
| | 10->3的边权v[fl]待求
↓ ↓ 由向量加减法易得:value[0]+v[fl]==v,即value[fl]= v-value[0],抽象得 value[fl]=v-value[l]
3
矩形关系时 l,r,v已给出,v为l->r的边权,求fl->fr边权value[fl]
fl——→fr
↑ ↑ l->fl边权为value[l]
| | r->fr边权为value[r]
| | l->r边权为v
l———>r 由向量加减法易得:value[l]+value[fl]=v+value[r] 则value[fl]=v+value[r]-value[l]
属于同一个并查集时算法推导,例如下面案例 l=3 r=6 v=41 fl=3 fr=3 属于同一并查集,现要判断给出的v是否满足方程,方程推导如下 (第四行输入)
10 3->6 题中给出v=41
↙ ↑ 6->10->3 value[6]=28-68=-40
↙ | value[3]=0
3———→6 如果value[3]==value[6]+v 即0==-40+41 则题目正确 因此,悖论+1条件为value[l]!=value[r]+v
那么为什么是v[l]不是0呢?
10 3->1 6->10->3->1
↙ ↑ 由于findFather自带路径压缩 ,所以3->1,6->1,而3->1是3->1和6->1的公共部分,减去之后变为上述情况
↙ | 因此方程为6->1 - 3->1 + 3->6==0 即value[r]-value[l]+v==0 ,即value[l]==value[r]+v
1<--3———→6
input
10 5
1 10 100 0 10 100
7 10 28 6 10 28
1 3 32 0 3 32
4 6 41 3 6 41
6 6 1 5 6 1
output
1
#include<cstdio>
#include<iostream>
using namespace std;
const int N=200005;
int father[N];//并查集用数组实现,根节点的话father[i]=i
int value[N];//记录权值
int ans;
void inf(int n)//初始化 易错点:i必须从0开始!! 因为l--,1会变0,所以初始化必须涉及0!! 教训,以后初始化从0开始,即使用不到也不亏
{
for(int i=0;i<=n;i++){
father[i]=i;
value[i]=0;
}
}
int findFather(int x)//路径压缩
{
if (x != father[x])
{
int t = father[x];
father[x] = findFather(father[x]);
value[x] += value[t];
}
return father[x];
}
void Union(int l,int r,int v)//合并 先判断它们的根节点fl与fr是否相同
{
int fl=findFather(l);
int fr=findFather(r);
if(fl==fr)//是则说明两元素同属一个并查集,判断给出的偏移量与算出的偏移量是否相等,不相等则ans++,方程为value[l]==value[r]+v
{
if(value[l]!=value[r]+v) ans++;
}else//否则考虑合并,把fl的父节点赋值为fr,因为并查集的合并操作核心是对根节点的操作,fl的权值value[fl]=v+value[r]-value[l]
{
father[fl]=fr;
value[fl]=v+value[r]-value[l];
}
}
int main(){
int n,m;
while(scanf("%d%d",&n,&m)!=EOF){
ans=0;
inf(n);
while(m--)
{
int l,r,v;
cin>>l>>r>>v;
l--;//变闭区间为开区间,方便连通使得并查集合并
Union(l,r,v);
}
cout<<ans<<endl;
}
return 0;
}