BZOJ.2844.albus就是要第一个出场(线性基)

题目链接

(Description)

给定一个集合A,将 所有子集中的所有数的异或和(包括空集) 从小到大排序,求一个数K在其中的排名(保证出现)。

(Solution)

构造出线性基,然后只保留线性基中存在数的位。
类似数位DP,对K二进制分解,如果K的当前位为1,那么先不选这一位,则后面的(i-1)位任意选择都比K小,且有(2^i)种方案(包括空集),加给答案。
但是在不去重的情况下,每个数可能出现多次。如果线性基的大小为(size),原集合大小为(n),那么线性基中 每个数都出现了(2^{n-size})次。
因为存在(n-size)个数,它们在线性基中可以唯一表示出来。那么对于一个数(x),从这(n-size)个数中任选一些Xor,总有唯一的方案使得在线性基中再异或后仍为(x)
所以答案再乘上(2^{n-size})就行了。
注意对K分解是在线性基上存在数的对应位上分解。

//1016kb	160ms
#include <cstdio>
#include <cctype>
#include <algorithm>
//#define gc() getchar()
#define MAXIN 200000
#define gc() (SS==TT&&(TT=(SS=IN)+fread(IN,1,MAXIN,stdin),SS==TT)?EOF:*SS++)
#define Bit 30
#define mod 10086
#define Double(x) ((x+=mod)>=mod)&&(x-=mod)

int n,size,base[36],cnt,A[36];
char IN[MAXIN],*SS=IN,*TT=IN;

inline int read()
{
	int now=0;register char c=gc();
	for(;!isdigit(c);c=gc());
	for(;isdigit(c);now=now*10+c-'0',c=gc());
	return now;
}
inline int FP(int x,int k)
{
	int t=1;
	for(; k; k>>=1, x=x*x%mod)
		if(k&1) t=t*x%mod;
	return t;
}
inline void Insert(int x)
{
	for(int i=Bit; ~i; --i)
		if(x>>i & 1)
			if(base[i]) x^=base[i];
			else {base[i]=x, ++size; break;}
}

int main()
{
	n=read();
	for(int i=1; i<=n; ++i) Insert(read());
	int K=read();
	for(int i=0; i<=Bit; ++i) if(base[i]) A[cnt++]=i;
	int ans=0;
	for(int i=0; i<cnt; ++i)
		if(K>>A[i] & 1) ans+=1<<i;
	printf("%d
",(ans%mod*FP(2,n-size)%mod+1)%mod);//排名加1啊→_→

	return 0;
}
原文地址:https://www.cnblogs.com/SovietPower/p/9292621.html