51nod 异或凑数

此题是一道有关 异或的秩的题目,已经有解题报告了,不过感觉解题报告里没看到 严格的证明,所以记录下自己的思路。


这道题要算给定区间内能否存在数异或成给定的数K。

首先数目达到500000,所以要考虑线性算法,当然nlogn也是可以的,不过一般都是希望O(n)。

一开始没什么思路,不过后来发现给定区间里的选择的数的数目不定,并且并不要求连续,还觉得比较困难。

想到异或有性质就是 秩 只跟bit数位有关,也就是说此题中rank最多是在30左右,选的数其实最多也就是这个数目即可。

然后以前其实做过类似题型:颜色染料。 有关一堆数的异或可以弄一个gauss消元矩阵来搞定,从而某数是否能由前面的一堆数异或得到也是可以做到的。


解题报告里只是说明了用gauss消元,并且考虑位置靠后的基,并且说可以递推进行处理,想过去是合理的,但并没说明为什么一定是位置靠后的基,而且为什么靠后的基之间可以递推得到,以及基与异或结果是否存在之间的联系,这些都是要考虑的。


题目要求某区间内是否能异或得到值,那么,可以求该区间的秩。但由于区间数有n*n种,所以想是否可以递推得到,但递推后面考虑是可行的,但只能得到所有1,i]区间的结果,但题目要求[j,i],那么只要记录线性无关基的位置,考虑是否能由[1,i]的结果推到[j,i]的结果。


考虑线性无关基的递推的时候,假设已经求得到index为i处的线性无关的一组基,由于基的选择有多种,想到可以求位置最前的那组,或者最后的那组,当然两者其实是等价的。不过一般我们是从前往后处理,而这个时候选择最前还是最后,两者其实都考虑了,不过最后排除了最前,选择取最后的那组,因为我们要求所有[j,i]的基,那么就必须从i开始往1得到线性无关基,这样[1,i]与[j,i]其实线性无关基就是部分相同的了


假设[1,i]区间rank为r,那么基有r个,由于选取位置靠后的,那么从i到1的基的位置可以设为i1,i2,...,ir,所有[j,i]可以据此截断得到。

现在考虑[1,i+1],假设该区间rank为r+1,那么基就是i+1, i1, i2,...,ir。

如果rank仍为r,也就是说i+1位置与之前某段是线性相关的。这里可以发现新的基与之前的基只有1个数是不同的,即 i+1,i1,i2,ik-1,  ik+1,...,ir。也就是将某个ik去掉,加上i+1。(草稿纸上演算过程就是从i+1处往1考虑,就能得到该结论)

以看到以上的思路没涉及到gauss消元。 也就是说,我们只要能记录基的位置;并按以上方法递推修改基德位置;以及快速计算基是否能组成所要求得的数K。


后面就是解题报告里的内容,利用gauss消元的方法。


由基得到所要求得的数K,暂时我的知识储备只能想到gauss消元的方法。  也就是说求得一组基的gauss消元矩阵,其实就是一组新基,只是该基容易计算。

一个数K可以得到gauss消元新基的组成,但我们要的是原基德异或组成。所以需要记录下来新基与旧基之间的构成关系,也就是解题报告里为什么要用到变换矩阵C。


基的递推过程,现在需要在新基里体现,并且这之间的联系推导是可以证明做到的,结论就是解题报告里体现的做法。



我也考虑了一些可能,比如gauss消元消的程度如何,是否可以压缩存储,快速计算等等。 但最终没进一步的成果了。


最终时间复杂度就是如解题报告里所说的那样,O(n*maxbitsize),即O(30n)


我的AC代码:

#include<iostream>

using namespace std;

#define maxsize 500001
int n, m, l, r, k, index, j, tmp_index, i;
int a[maxsize], query[maxsize][3], d[maxsize]; //aa[maxsize]
int *que[maxsize];
bool result[maxsize];
#define bitsize 32
int gauss[bitsize], gauss_rank = 0, tmp, b[bitsize], min_index, c[bitsize];

void read(int &ans){
	ans = 0;
	char last = ' ', ch = getchar();
	while (ch >= '0' && ch <= '9')ans = ans * 10 + ch - '0', ch = getchar();
}

inline void getInput()
{
	/*	tmp = 1;
	for (int i = 1; i < bitsize; ++i)
	{
	p[tmp] = i;
	tmp <<= 1;
	}*/

	scanf("%d", &n);
//	n = read();
//	read(n);
	//cout << n << endl;
	for (int i = 1; i <= n; ++i)
	{
	scanf("%d", &a[i]);
//		a[i] = read();
//			read(a[i]);
		//	aa[i] = a[i];
		//	cout << a[i] << endl;
	}
	scanf("%d", &m);
//	m = read();
//	read(m);
	
	for (int i = 0; i < m; ++i)
	{
		scanf("%d%d%d", &query[i][0], &query[i][1], &query[i][2]);
//		query[i][0] = read();
//		query[i][1] = read();
//		query[i][2] = read();
//		read(query[i][0]);
//		read(query[i][1]);
//		read(query[i][2]);
		++d[query[i][1]];
	}

	for (int i = 1; i <= n; ++i)
	{
		que[i] = new int[d[i] + 1];
		que[i][0] = 0;
	}

	for (int i = 0; i < m; ++i)
	{
		que[query[i][1]][++que[query[i][1]][0]] = i;
	}

}

inline bool check_is_highest_bit_not_same(int &x, int &y)//
{
	tmp = x ^ y;
	if ((tmp >= x) || (tmp >= y))
	{
		return true;
	}
	return false;
}

inline void check_rank(int &x)//
{
	tmp_index = 0;
	
	for (j = 1; j <= gauss_rank + 1; ++j)
	{
		//cout << j << '\t' << gauss_rank << '\t' << x << '\t' << gauss[j] <<'\t'<<tmp_index<<'\t'<<c[j]<< endl;
		if (gauss[j] <= x)
		{
			if (check_is_highest_bit_not_same(gauss[j - 1], x))
			{
				if (check_is_highest_bit_not_same(gauss[j], x))
				{
					//cout << gauss[j] << '\t' << x << endl;
					
					break;
				}
			}
			else
			{
				--j;
				
			}
			x ^= gauss[j];
			tmp_index ^= c[j];
			//cout << x << endl;
		}
	}

}

inline void processInput()
{
	check_rank(a[i]);
	if (a[i])//上一步中,a[i]比gauss[j]大,跳出,即,线性无关
	{
		++gauss_rank;
		for (int k = gauss_rank; k > j; --k)
		{
			gauss[k] = gauss[k - 1];
			c[k] = c[k - 1];
		}
		c[j] = tmp_index ^ (1 << (gauss_rank - 1));
		gauss[j] = a[i];
		b[gauss_rank] = i;
	}
	else//线性相关
	{
		min_index = 0;
		index = tmp_index;
		tmp = 1;
		while (tmp_index)
		{
			while (!(tmp_index & 1))
			{
				tmp_index >>= 1;
				++tmp;
			}
			/*tmp = p[(tmp_index ^= (tmp_index - 1)) + 1];*/
			if (b[tmp] < b[min_index])
			{
				min_index = tmp;
			}
			tmp_index >>= 1;
			++tmp;
		}		//找到最前的一个数

		b[min_index] = i;
		tmp_index = (tmp = 1 << (min_index - 1)) ^ index;
		for (int j = 1; j <= gauss_rank; ++j)
		{
			if (c[j] & tmp)
			{
				c[j] ^= tmp_index;
			}
		}
	}
}

inline void processOutput()
{
	for (int k = 1; k <= que[i][0]; ++k)
	{
		//cout << i<<'\t'<<query[que[i][k]][2] << endl;
		check_rank(query[que[i][k]][2]);
		if (query[que[i][k]][2])
		{
			
			//cout << "线性无关" << endl;
			result[que[i][k]] = false;
		}
		else
		{
			tmp = 1;

			result[que[i][k]] = true;
			while (tmp_index)
			{
				while (!(tmp_index & 1))
				{
					tmp_index >>= 1;
					++tmp;
				}
				//	cout << "index" << b[tmp] << '\t' << query[que[i][k]][0]<< endl;
				if (b[tmp] < query[que[i][k]][0])
				{
					result[que[i][k]] = false;
					break;
				}
				tmp_index >>= 1;
				++tmp;
			}			//找到最前的一个数
		}
	}
}

inline void process()
{
	gauss[0] = b[0] = 2147483647;
	
	for (i = 1; i <= n; ++i)
	{
		processInput();
		processOutput();
	}
}

inline void getOutput()
{
	for (int i = 0; i < m; ++i)
	{
		if (result[i])
		{
			printf("YES\n");
		//	puts("YES\n");
		}
		else
		{
		//	puts("NO\n");
			printf("NO\n");
		}
	}
}
int main()
{
	//	freopen("F:\\TE5.txt", "r", stdin);
	//	freopen("F:\\Aoutputlarge1.txt", "w", stdout);

	getInput();

	process();

	getOutput();
	//	cout << "llllll" << endl;
	//	scanf("%d", &n);
	return 0;
}


代码里当时还考虑怎么gauss消元,确定最高位1的位置。但事实上gauss消元矩阵其实直接开一个30个数组即可。

而不是让gauss消元矩阵维数是由1开始变化的,还要考虑怎么插入,可能也因此,代码写得复杂了。  不过好歹AC了




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值