并查集-区间问题

 题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3038

题目大意:给出许多个区间的和,问你在前面给出的天台见都正确的情况下,当前条件是假的 的条数有多少个

题目分析:一般看到并查集的题目,首先要想到怎么去构造,构造的内容有俩个方面,

一个是树的根节点的合并,一个是单个树的状态压缩。

对于这题,比较经典,我们首先来分析第一个,单个树的状态压缩:

a->b->c->d   首先肯定有个这样的树 那么我们现在要实现find(a)的话就要使得结果达到这样一个效果,那就是 使得这个树只有俩层 ,a->d b->d c->d ,那么对于这个带权并查集怎么实现。我们想到了递归 ,用一个数组来保存当前节点到他的父节点的区间和,那么通常的这个递归算法就是先递归到底 然后再回溯逐层求解的过程,那么我们需要从a先递归到d(这也是并查集板子状态压缩的思路),但是最后我们的数组v[a]保存的就成了a到d的区间和 ,那么这就很简单了,用递归求出前面的区间和[b,d] 然后v[a] = v[a] +v[b] 即可 

那么对于第二个 ,根节点的合并怎么处理:

首先 我们又俩颗树 要实现函数add(int a,int b)

那么我们传统的思路就是先找到a和b的根节点 ,即 调用find函数 ,这个函数同时也实现了路径压缩的功能,使得你的俩颗树变成了 a-->ra b-->rb 那么现在有个关系就是你要合并[a,b] 那么我们可以在这俩个图之间画一个循环出来 ,类似于大学物理里面的电势下降相等   即             ra->a  ra->rb  rb->b a->b  那么这个是合并之后的 ,大家可以自己在纸上画出一个循环的图 那么沿着箭头方向 下降的高度一致 则得到了这个结论

s[a]+[a,b] ==s[r] + [rb,ra]  于是[rb,ra] 就是我们要唯一更新的那个值因为只有rb的状态变化。所以[rb,ra] = sa-sr+s(a,b) 

对于这题,我们还要注意一个问题就是 ,题目给的是闭区间的区间和,那么我们在状态压缩的时候啊,就会使得边界的值发生重复 叠加的情况,那么我们这么优化下这个并查集让 父节点是区间的(左边界-1),子节点是右边界,那么我们在s做+=的时候就不会计算俩次了。(这个思路看上去就是根节点是较小的那个边界 子节点则越来越大)

//想法  :对于并查集  其实质就是一颗树  那么我们在求解的时候 用树的性质去维护就可以了 
#include<iostream>
using namespace std;
#define maxn 200005
int pre[maxn];
int sn[maxn];//维护一个和    a------aa------aaa (根)   
void init()
{
	for(int i=0;i<=maxn-1;i++)
	{
		pre[i]=i;
		sn[i]=0;
	}
}
int find(int x)
{
	if(x!=pre[x])
	{
		int father = pre[x];
		pre[x] = find(pre[x]);//找到根节点  一路 
		sn[x] += sn[father];//
	}
	return pre[x];//
}
int main()
{
	int n,m;
	while(scanf("%d%d",&n,&m)!=EOF)
	{
		int ans=0;
		init();
		for(int i=1;i<=m;i++)
		{
			int a,b,s;
			scanf("%d%d%d",&a,&b,&s);
			a--;// [a,ra]
			int aa=find(a);
			int bb=find(b);
			if(aa!=bb)//如果不是一个集合 合并 
			{
				pre[aa]=bb;
				sn[aa]= sn[b]-sn[a]+s;//俩个区间根的差 a----------ra //sn[a]
									//              s:(	b)-------(---rb) //sn[b]  sn[b]-sn[a]+s
				
			}
			else//同一个集合 
			{
				if(sn[a]-sn[b]!=s)
					ans++;
			}
		}
		printf("%d\n",ans);
	}
}




评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值