poj1733 离散化 带权并查集的思考

poj1733解析 离散化  带权并查集的思考

题意是给你一个区间和区间1的个数是偶数还是奇数 然后判断第一个错提问的   第一眼看到这题感觉是线段树 思考一下线段树的做法 线段树维护区间信息 维护一个区间是奇数还是偶数 线段树一个节点代表一个区间 但是一个区间并不代表一个节点 要多个节点存储一个信息 多个交叉区间维护的话 就乱掉了 而且一般线段树题是多次询问 所以线段树不行,这个题就是维护区间信息

那么维护数据信息的数据结构有哪几种 1:线段树(排除) 2:排序二叉树(相关的也不用想)3:优先队列(不用想)4;并查集(哎,这个可以)。。。好像所有的数据结构都是用来维护数据的偷笑偷笑偷笑偷笑

用并查集无非就是创建集合 集合中的元素在某种关系下是平等的 所以构成一个集合 并查集就是维护节点与节点的信息和节点与根节点的关系这两种维护方法 

这道题是一个区间?这就有点怪了 区间怎么用并查集维护?  节点与节点吗? 不行! 如果节点与节点代表一个区间 那么一个集合的多个节点 我怎么知道哪个是哪个区间 所以不能维护  那么就是节点与根节点的 根节点与子节点构成一个区间 埃 这好像也不行啊 根节点只有一个啊  如果我区间有一个是子节点 而另一个不属于这个集合 那我怎么维护啊 比如 【1,3】这个操作完之后就是1为根结点 3是子节点 如果这时候【3,4】那么怎么维护啊  其实这个就是带权并查集 本来是【3,4】经过带权并查集操作 变成【1,4】的信息 还有就是这道题给的是两段都是闭区间 要左区间减1变成半闭半开 要不然不能并(这个其实刚开始看的时候很难想出来,对我来说)

这代替维护的就是奇偶性

#include<iostream>
#include<string.h>
#include<math.h>
#include<stdio.h>
#include<algorithm>
using namespace std;
#define maxn 5005
int pa[maxn],d[maxn],v[maxn],co=1,k=1;
long long le[maxn],re[maxn],p[2*maxn],ans;
long long find(int x)
{
	if(pa[x]!=x)//带权压缩的
	{
		long long t=find(pa[x]);//这里必须是一步一步的更新节点 不能直接更新到根结点 应为下面的
//合并操作 仔细思考就出来了
               d[x]^=d[pa[x]];//异或 相同的就是0,不相同的就是1; 
		return pa[x]=t;
	}
	else
		return x;
}
int unite(int lx,int rx,int qu)
{
	int fx=find(lx),fy=find(rx);
	if (fx == fy)  
    {  
        if (d[lx]^d[rx]!=qu)  
            return true;    
        ans++;  
    }  
    else  
    {  
        pa[fx]=fy;  
        d[fx]=d[lx]^d[rx]^qu;  //z这个也思考错了
//d[lx] d[rx]是到根节点的关系 具体可看下面的图
       ans++;
    }  
    return false;
}
int main()
{
	long long tot;
	int n;char ch[6];
	while(scanf("%I64d",&tot)!=EOF)
	{
		scanf("%d",&n);co=0;k=0;ans=0;
		for(int i=0;i<n;i++)
		{
			scanf("%I64d %I64d %s",&le[i],&re[i],ch);
			le[i]--;
			if(ch[0]=='e') v[i]=0;
			else v[i]=1;
			p[co++]=le[i];p[co++]=re[i];
		}
		sort(p,p+co);  
        k=unique(p,p+co)-p;  
        for(int i=0;i<k;i++)
			pa[i]=i,d[i]=0;
		/*for(int i=1;i<k;i++)
			printf("%d ",p[i]);
		puts("");*/
		for(int i=0;i<n;i++)
		{
			int lx=lower_bound(p,p+k,le[i])-p;
			int rx=lower_bound(p,p+k,re[i])-p;
			//cout<<"lx="<<lx<<"rx="<<rx<<"v[i]="<<v[i]<<endl;
			if(unite(lx,rx,v[i]))
				break; 
		}
		printf("%d",ans);
	}
	return 0;
} 

因为fx的子节点d[x]到fx的关系是正确的 只要改变d[fx]就行了 后面的子节点后面find函数的会更新 这就是上面为什么不要一步直接更新到根结点的原因 按一个公式 fx->fy =d[fx]  x->fx =d[x] y->fy=d[y] x->y =qu

看图-》在x y 之间添加一条边x指向y 然后x 和 fx 之间的变成反向边 看图-》可得d[fx]=d[y]-d[x]+qu  因为这道题是奇偶性 你对我是偶 那么我对你也是偶  所以加减无所谓了   这是带权并查集的合并


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值