CF251D - Two Sets 题解

考虑线性基求出来之后尝试发明一个针对这题这个双重关键字问题的按位贪心方法。

首先发现两组异或和是确定的 \(S\),如果第一组是 \(x\),第二组就是 \(x\oplus S\),答案的两个关键字此时就是 \((x+x\oplus S,x)\)

从高往低看,如果 \(S\) 当前这一位是 \(0\),那么如果 \(x\) 这一位取 \(1\)\(x\oplus S\) 这一位也是 \(1\),在这一位对第一关键字贡献是 \(2^{i+1}\);如果都取 \(0\),那么即使后面位全部取 \(1\),那最大也只能是 \(2(2^i-1)\)。所以如果 \(S\) 当前这一位是 \(0\)\(x\) 这一位一定是要取 \(1\) 的(如果有得选的话)。

但如果 \(S\) 当前这一位是 \(1\) 那就值得犹豫了,因为无论 \(x\)\(0\) 还是 \(1\) 对第一关键字的贡献都是一样的,如果立刻考虑第二关键字应该是要取 \(0\) 的。但关键是这一位取或不取对后面有影响啊,我们不知道取 \(0\) 会不会使得以后第一关键字变逊了,这就不好办了。

其实担心的东西就是现在考虑第二关键字会不会对第一关键字产生影响。那说明这个按位贪心还不合格,即使是从高往低位贪心仍然排除不掉后效性。那可以把那些影响第一关键字的位——\(S\)\(0\) 的位提到前面来决定完之后,再去管第二关键字。所以只需要把各位的地位换一下然后求线性基贪心即可。

求出 \(x\) 的值之后还要还原出在原数组里的选择。这个就是标准的高斯消元解矩阵方程了。虽说线性基和高斯消元本质上是一样的,但是线性基是横着高斯消元啊……终究取代不了高斯消元。所以还要写个 bitset 优化高消,总复杂度线对。

code
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define mp make_pair
#define X first
#define Y second
const int inf=0x3f3f3f3f3f3f3f3f;
const int N=100010;
int n,xsm;
int a[N];
int b[N],w[N];
bool cmp(int x,int y){
	if((xsm>>x&1)!=(xsm>>y&1))return (xsm>>x&1)>(xsm>>y&1);
	return x<y;
}
void insert(int x){
	for(int i=60;~i;i--)if(x>>w[i]&1)
		if(b[w[i]])x^=b[w[i]];
		else{b[w[i]]=x;break;}
}
bitset<N> mt[70];
void gauss(){
	for(int i=1;;i++){
		pair<int,int> p(inf,inf);
		for(int j=i;j<=61;j++)if(mt[j].any()){
			int x=mt[j]._Find_first();
			p=min(p,mp(x,j));
		}
		int col=p.X,row=p.Y;
		if(col==inf)break;
		swap(mt[i],mt[row]);
		for(int j=1;j<=61;j++)if(i!=j&&mt[j][col])mt[j]=mt[j]^mt[i];
	}
}
int otp[N];
signed main(){
	cin>>n;
	for(int i=1;i<=n;i++){
		scanf("%lld",a+i);
		xsm^=a[i];
	}
	for(int i=0;i<=60;i++)w[i]=i;
	sort(w,w+61,cmp);
	for(int i=1;i<=n;i++)insert(a[i]);
	int ans=0;
	for(int i=60;~i;i--)
		if(!(xsm>>w[i]&1)){
			if(!(ans>>w[i]&1))ans^=b[w[i]];
		}
		else{
			if(ans>>w[i]&1)ans^=b[w[i]];
		}
//	cout<<ans<<"!\n";
	a[++n]=ans;
	for(int i=1;i<=n;i++)for(int j=1;j<=61;j++)if(a[i]>>j-1&1)mt[j].set(i);
	gauss();
	for(int i=1;i<=61;i++)if(mt[i].any()){
		int x=mt[i]._Find_first();
		if(mt[i][n])otp[x]=1;
	}
	for(int i=1;i<n;i++)printf("%d ",otp[i]?1:2);
	return 0;
}
posted @ 2021-08-16 10:45  ycx060617  阅读(54)  评论(0编辑  收藏  举报