首页 > 代码库 > BZOJ 2844 albus就是要第一个出场 高斯消元

BZOJ 2844 albus就是要第一个出场 高斯消元

题目大意:给定一个n个数的集合S和一个数x,求x在S的2^n个子集从大到小的异或和序列中最早出现的位置

有学长真好不用自己打题目大意了233

首先我们求出线性基 我们会得到一些从大到小排列的数和一堆0 记录0的个数

不考虑0,看前面的数,由于线性基的性质,我们直接贪心从大到小枚举 若当前异或和异或这个值小于Q则取这个数 (注意^不要写成+或者| 本蒟蒻已经因为这个WA了两道题了

然后我们通过每个数取不取可以得到一个01序列 这个序列就是通过异或可以得到的小于Q的数的数量的二进制

比如线性基是8 4 2 Q=13 取完8和4之后无法法取2 那么得到的01序列就是110 即6

然后我们再加上空集的0 

然后考虑后面的0 设有m个0 那么每个数出现的次数为2^m 乘起来后+1就是答案

一个细节就是当Q=0的时候计算小于Q的数的数量时不要算上0 不懂的可以自己测测0

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define M 100100
#define Mo 10086
using namespace std;
int n,m,q,ans,a[M];
void Gaussian_Elimination()
{
	int i,j,k=0;
	for(j=1<<30;j;j>>=1)
	{
		for(i=k+1;i<=n;i++)
			if(a[i]&j)
				break;
		if(i==n+1)
			continue;
		swap(a[i],a[++k]);
		for(i=1;i<=n;i++)
			if(i!=k)
				if(a[i]&j)
					a[i]^=a[k];
	}
	m=n-k;
	n=k;
}
int ksm(int x,int y)
{
	int re=1;
	while(y)
	{
		if(y&1)re*=x,re%=Mo;
		x*=x,x%=Mo;
		y>>=1;
	}
	return re;
}
int main()
{
	int i,num,now=0;
	cin>>n;
	for(i=1;i<=n;i++)
		scanf("%d",&a[i]);
	Gaussian_Elimination();
	cin>>q;
	for(i=1;i<=n;i++)
		if( (now^a[i])<q )
			ans+=ksm(2,n-i),ans%=Mo,now^=a[i];
	if(q)
		++ans,ans%=Mo;
	ans*=ksm(2,m),ans%=Mo;
	++ans,ans%=Mo;
	cout<<ans<<endl;
}


BZOJ 2844 albus就是要第一个出场 高斯消元