51nod 异或凑数

来源:互联网 发布:淘宝手机搜索不精准 编辑:程序博客网 时间:2024/04/30 13:03

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


这道题要算给定区间内能否存在数异或成给定的数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 500001int 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 32int 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
原创粉丝点击