带权并查集 HDU 3038

HDU 3038

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

#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;
}

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值