[BZOJ2844]albus就是要第一个出场

题目

传送门

题解

对于这道题,我们有一个十分强大的结论,先给出这个结论,再给出一个比较感性的证明。

假设我们有一个集合 \(A\),其中 \(|A|=n\),而 \(A\) 的线性基我们记作 \(G\),且 \(|G|=k\),那么对于 \(A\) 所有子集 \(S_i(1\le i\le 2^n)\) 的异或和只会有 \(2^{k}\) 个值,每个值出现的次数为 \(2^{n-k}\) 次。

怎么证明这个结论呢?

我们可以考虑,子集 \(S_i\) 一共有 \(2^n\) 的异或和,但是由 \(G\) 最多可以得出 \(2^k\)不同的值,而由线性基的特性,由 \(G\) 得出的值和由 \(A\) 得出的值都是一样的,线性基即原集合的压缩,但是我们实际上可以得到 \(2^n\) 个值,那么 \(2^k\)\(2^n\) 这巨大的差值之间,一定代表 \(2^n\) 个数会有重复,去重之后就可以得到 \(2^k\) 个数,说明每个数重复出现 \(2^{n-k}\) 次。

是不是十分感性,但是似乎也只能这么说了

对于这道题,我们只需要看一下 \(Q\) 到底是排名为多少的数,得到其排名 \(rk\) 之后,直接输出 \(rk\times 2^{n-k}+1\) 即可。

#include<bits/stdc++.h>
using namespace std;

#define rep(i,__l,__r) for(signed i=(__l),i##_end_=(__r);i<=i##_end_;++i)
#define fep(i,__l,__r) for(signed i=(__l),i##_end_=(__r);i>=i##_end_;--i)
#define writc(a,b) fwrit(a),putchar(b)
#define mp(a,b) make_pair(a,b)
#define ft first
#define sd second
#define LL long long
#define ull unsigned long long
#define uint unsigned int
#define pii pair< int,int >
#define Endl putchar('\n')
// #define int long long
// #define int unsigned
// #define int unsigned long long

#define cg (c=getchar())
template<class T>inline void qread(T& x){
    char c;bool f=0;
    while(cg<'0'||'9'<c)f|=(c=='-');
    for(x=(c^48);'0'<=cg&&c<='9';x=(x<<1)+(x<<3)+(c^48));
    if(f)x=-x;
}
template<class T>inline T qread(const T sample){
    T x=0;char c;bool f=0;
    while(cg<'0'||'9'<c)f|=(c=='-');
    for(x=(c^48);'0'<=cg&&c<='9';x=(x<<1)+(x<<3)+(c^48));
    return f?-x:x;
}
#undef cg
// template<class T,class... Args>inline void qread(T& x,Args&... args){qread(x),qread(args...);}
template<class T>inline T Max(const T x,const T y){return x>y?x:y;}
template<class T>inline T Min(const T x,const T y){return x<y?x:y;}
template<class T>inline T fab(const T x){return x>0?x:-x;}
inline int gcd(const int a,const int b){return b?gcd(b,a%b):a;}
inline void getInv(int inv[],const int lim,const int MOD){
    inv[0]=inv[1]=1;for(int i=2;i<=lim;++i)inv[i]=1ll*inv[MOD%i]*(MOD-MOD/i)%MOD;
}
template<class T>void fwrit(const T x){//just short,int and long long
    if(x<0)return (void)(putchar('-'),fwrit(-x));
    if(x>9)fwrit(x/10);
    putchar(x%10^48);
}
inline LL mulMod(const LL a,const LL b,const LL mod){//long long multiplie_mod
    return ((a*b-(LL)((long double)a/mod*b+1e-8)*mod)%mod+mod)%mod;
}

const int MAXN=1e5;
const int MOD=10086;

template<class T>struct Basic{
    #define MAXSIZE 62
    T f[MAXSIZE+5],g[MAXSIZE+5];
    int flg,siz;
    //flg 记录插入时是否有不能放入的数
    inline void clear(){
        memset(f,0,sizeof f);flg=siz=0;
    }
    inline void insert(T num){
        fep(i,MAXSIZE,0){
            if(!(num>>i))continue;
            if(!f[i]){f[i]=num;++siz;return;}
            num^=f[i];
        }
        ++flg;
    }
    inline T queryMax(T ret){
        fep(i,MAXSIZE,0)if((ret^f[i])>ret)
            ret^=f[i];
        return ret;
    }
    inline T queryMin(){
        if(flg)return 0;
        T ret=(1ll<<MAXSIZE);
        rep(i,0,MAXSIZE)ret=Min(ret,f[i]);
    }
    inline void buildg(){
        rep(i,0,MAXSIZE)g[i]=f[i];
        rep(i,0,MAXSIZE)rep(j,0,i-1)if(g[i]&(1ll<<j))
            g[i]^=g[j];
    }
    inline T queryNum(T k){
    //must build g before use it
        if(flg)--k;
        if(k==0)return 0;
        T ret=0;
        rep(i,0,MAXSIZE)if(g[i]){
            if(k&1)ret^=g[i];
            k>>=1;
        }
        return k?-1:ret;
    }
};
Basic<LL>G;

inline int qkpow(int a,int n,const int mod){
	int ret=1;
	for(;n>0;n>>=1){
		if(n&1)ret=ret*a%mod;
		a=a*a%mod;
	}
	return ret;
}

int pos[MAXSIZE+5],cnt,n;

LL Q,ans;

signed main(){
	n=qread(1);
	rep(i,1,n)G.insert(qread(1ll));
	Q=qread(1ll);
	rep(i,0,62)if(G.f[i])pos[cnt++]=i;
	rep(i,0,cnt-1)if(Q&(1ll<<pos[i]))
		(ans|=(1<<i))%=MOD;
	printf("%lld\n",((ans%MOD*qkpow(2,n-G.siz,MOD))+1)%MOD);
    //不能直接用左移,因为会爆 long long
	return 0;
}
posted @ 2020-05-12 16:31  Arextre  阅读(130)  评论(0编辑  收藏  举报