[数据结构][带权并查集][异或] UVALive 4487 Exclusive-OR

一道不是那么传统的带权并查集

题目特殊的地方在于运算是异或,不是像经典的带权并查集那样可以有一个恒定的子节点与父结点的递推关系。比如传统的求一个结点到根结点的距离的并查集可以这样写

int findfat(int x)
{
	if(fat[x] == x) return x;
	int tmp=fat[x];
	fat[x]=findfat(fat[x]);
	//在此处修改val比如:
	value[x]=value[tmp]+1;
	return fat[x]; 
}

这样写的正确性在于这个递推关系一定是正确的,即使在森林里改变任意一个点的数值,在他的后代执行findfat的时候也一定能更新所有的数值。但是异或是不太一样的,如果直接写 value[x]=value[x]^value[tmp],的话,仅仅是对x求两次查找根结点而不做任何其他操作就会使得数值出现错误,因为w[x]等于和自己又异或了一次,就会出现不正常的结果。

思考了一下,我直接把w数组定义为:w[i]表示 继续阅读“[数据结构][带权并查集][异或] UVALive 4487 Exclusive-OR”

[线段树+][并查集][区间信息维护] UVALive 4730 Kingdom

利用并查集处理合并,并保存每个块对应的覆盖范围(在y轴上的)上下界;利用线段树(更新区间的范围从并查集维护的信息中得到)维护每个x.5的点对应的州数和城市数。区间修改单点查询,只要确定好x.5这些点在线段树中的对应的下标就可以了,我这里是x.5在线段树中下标为(x+1)。由于我写的是1-n的线段树写完了才发现题目说的0-n-1,只能把读进来的东西都++。具体的操作:对于road,先找两个点是否在一个块里,如果是了那就不用任何操作(画图可以看出来如果这俩是一个块里的那么他们覆盖范围之间一定已经有线了),否则先把这两个的分别的块对应的区域在线段树中减去他的影响,再将两个块合并之后,再在线段树中加上这一新块的影响。先减后加,这样就可以避免繁杂的分类讨论了。。。

第一版正经的线段树ac之后想了想感觉在这个情境下只需要保证叶子节点的信息有效就行了,推了一下感觉可以去掉maintain的操作。。。试了下果然可以,而且快了很多,在vjudge上已经是用线段树的跑的最快的了(前几名都是用的BIT emmm

wa的点:本来一气呵成写完很舒服但是没想到出了很多bug,因为一些愚蠢的bug调试了很久。。。。数据结构这块还是太弱了。
继续阅读“[线段树+][并查集][区间信息维护] UVALive 4730 Kingdom”

[换轴][并查集][异或性质][连通分量] codeforces 1040E Network Safety

http://codeforces.com/contest/1040/problem/E

这个做法最重要的是原本的每一条边两端点一定不相等,所以两个点如果同时被异或结果一定仍然不相等。     然后就是考虑一个点被异或另一个点没有被异或的情况,一开始觉得会很复杂?但是实际上不是这样的,远没有想象中的复杂:  当x确定的时候,只有a⊕b==x的那些边会受到影响,这些边必须同时被选中,而剩下的,无所谓。。所以就把这些边连起来的点缩点之后就成了2^q的选择方式了。。

实现细节:①会卡并查集退化,所以必须写按秩合并   ②每次只要记录下被改动的点,然后下一次把这些点并查集复原即可,否则由于并查集的初始化时间复杂度会退化。

继续阅读“[换轴][并查集][异或性质][连通分量] codeforces 1040E Network Safety”