此题是一道有关 异或的秩的题目,已经有解题报告了,不过感觉解题报告里没看到 严格的证明,所以记录下自己的思路。
这道题要算给定区间内能否存在数异或成给定的数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了