【题解】[SCOI 2019] 函数【线性基】

题目链接

题意

给定 \(a_{1\cdots n},w\)。有 \(n\) 个 01 向量,第 \(i\) 个为 \(a_i,a_i+1,a_i+2,\cdots a_i+w\) 转化为一定长度 \(L\) 的二进制后拼接起来。求这些向量组成矩阵的轶(异或意义下)。\(n,w,a_i\leq 2^{17}\)

题解

尝试通过做列变换简化矩阵。

以每 \(L\) 列为一组,先把后一组异或上前一组,得到每行形如 xxxxxx|000111|000001|000011|000001|001111...

将组内每一列异或上前一列,得到每行形如 xxxxxx|000100|000001|000010|000001|001000...

找到 1 的位置最靠前的一组,这个 1(下面将其称为“关键 1”)的位置确定后,其他 1 的位置也都能确定。故以组内位置为第一关键字,组间顺序为第二关键字,挨个假设每个位置是关键 1,用这一列去异或在此假设下其他为 1 的列。在遇到真正的关键 1 之前,我们都在用 0 异或其他数,故没有影响;关键 1 会把其他所有位置变成 0,所以每行后面部分只剩一个关键 1。

开始线性基:先以关键 1 为主元,在后面消元;若有关键 1 相同的行,选定一行放入线性基,其他行异或它后用前 17 位进行标准的线性基。

#include<bits/stdc++.h>
using namespace std;
const int N=2e5+10,L=19,mod=998244353;
int n,w,a[N],b[L];
map<pair<int,int>,int>mp;
void ins(int x){
	for(int i=L-1;i>=0;--i){
		if((x>>i)&1){
			swap(b[i],x);
			if((x>>i)&1)x^=b[i];
		}
	}
}
int main(){
	int n,w;
	scanf("%d%d",&n,&w);
	for(int i=0;i<n;i++)scanf("%d",a+i);
	for(int i=0;i<n;i++){
		int k=a[i]^(a[i]+w),q=0;
		while(k>>q)++q;
		pair<int,int>p(q,(((a[i]>>(q-1))+1)<<(q-1))-a[i]);
		if(!w)ins(a[i]);
		else if(mp.count(p))ins(mp[p]^a[i]);
		else mp[p]=a[i];
	}
	int r=mp.size();
	for(int i=0;i<L;i++)if(b[i])++r;
	int t=1;while(r--)t<<=1,t>=mod&&(t-=mod);
	cout<<t;
}

posted @ 2020-12-25 20:54  破壁人五号  阅读(156)  评论(0编辑  收藏  举报