uoj#36. 【清华集训2014】玛里苟斯(线性基+概率期望)

传送门

为啥在我看来完全不知道为什么的在大佬们看来全都是显然……

考虑(k=1)的情况,如果序列中有某一个(a_j)的第(i)位为(1),那么(x)的第(i)位为(1)的概率就是(frac{1}{2})

证:把(a_j)拿出来,那么剩下的里面选出的子集不管是什么情况,(a_j)放进去或不放肯定有一种能使(x)的第(i)位为(1),且另一种使(x)的第(i)位为(0),那么概率就是(frac{1}{2})

然后是(k=2)的情况,就是个$$sum_{i=0}^{base}sum_{j=0}^{base}d_id_j2^{i+j}$$
其中(base)为最高位,(d_i,d_j)为这一位为(1)的概率。如果(i)(j)其中一个不存在则跳过。否则在考虑(i,j)在所有的数中出现的情况,如果对于每一个数,这两位的值都相同,说明这两个值不互相独立,那么同时为(1)的概率就是(frac{1}{2}),否则这两位互相独立,那么同时为(1)的概率是(frac{1}{4})

最后是(kgeq 3)的情况,这里有一个结论,异或值(x)取到所有能取的数的概率相等。大佬们都认为显然,然而我太菜了看不出来为什么,伪证一下好了

(n=|S|)(S)中线性基的大小为(Base),我们考虑在那些不在线性基中的元素取数,共有(2^{n-Base})中取法,对于每一种取法取到的值(x),线性基中有唯一对应的取法取到(x),所以在线性基中取数使得所有元素异或和为(0)的方案数是(2^{n-Base})

(x)能取到的每一个值(v)都可以被线性基中的元素唯一表示,记为(L),所有使异或和(x)(v)的集合一定是形如(L)异或上元素异或和为(0)的集合(T),所以取到每个(v)的方案数都是(2^{n-Base}),所以概率相等

于是我们直接搞出线性基,然后爆搜所有能异或出来的元素,每个元素的概率都是(1)除以元素个数

然后是关于小数的问题,(k=1,2)的时候根据运算过程可以发现小数位要么是(0)要么是(0.5)

然后是(kgeq 3)的时候小数位也最多是(0.5)(Bill Yang)巨巨的证明看不太懂,然后大米饼巨巨的证明勉强能看懂,证明如下

可以仔细分析一下k==2时的算法;
再扩展到k次方,发现在异或运算下:
二进制位之间贡献不相互独立是具有传递性的;
假设一次计算答案时选定的k个二进制位(可能相同分)集合为:
B  = {b1,b2,...bk}
我们可以把他们进一步分成m个集合:
S1...Sm
相同集合元素贡献不互相独立,不同集合贡献互相独立;
这时对答案期望的贡献应该是2^{b1+b2+...+bk - m}  ;
而k >= m , 且B里面至少有m个不同的二进制位(即bi!=bj这种);
所以考虑b1+b2+...+bk - m最小的情况:
分析可以发现最小为-1;
所以答案小数点后只有一位;

然后……没啥然后了……

//minamoto
#include<bits/stdc++.h>
#define R register
#define ll unsigned long long
#define fp(i,a,b) for(R int i=a,I=b+1;i<I;++i)
#define fd(i,a,b) for(R int i=a,I=b-1;i>I;--i)
#define go(u) for(int i=head[u],v=e[i].v;i;i=e[i].nx,v=e[i].v)
using namespace std;
char buf[1<<21],*p1=buf,*p2=buf;
inline char getc(){return p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++;}
ll read(){
    R ll res,f=1;R char ch;
    while((ch=getc())>'9'||ch<'0')(ch=='-')&&(f=-1);
    for(res=ch-'0';(ch=getc())>='0'&&ch<='9';res=res*10+ch-'0');
    return res*f;
}
const int N=1e5+5,Base=25;
int n,K,top;ll a[N],b[N];
ll st[Base+5];
void solve1(){
	ll res=0;
	fp(i,0,n-1)res|=a[i];
	printf("%llu",res/2);
	if(res&1)puts(".5");
}
void solve2(){
	ll ans=0,res=0;
	fp(i,0,31)fp(j,0,31){
		bool flag=0;
		fp(k,0,n-1)if(a[k]>>i&1){flag=1;break;}
		if(!flag)continue;
		flag=0;
		fp(k,0,n-1)if(a[k]>>j&1){flag=1;break;}
		if(!flag)continue;
		flag=0;
		fp(k,0,n-1)if((a[k]>>i&1)!=(a[k]>>j&1)){flag=1;break;}
		if(i+j-1-flag<0)++res;
		else ans+=1ll<<(i+j-1-flag);
	}
	ans+=res>>1,res&=1;
	printf("%llu",ans);
	if(res)puts(".5");
}
void solve3(){
	fp(i,0,n-1){
		fd(j,Base,0)
		if(a[i]>>j&1){
			if(b[j])a[i]^=b[j];
			else{
				b[j]=a[i],st[top++]=a[i];
				break;
			}
		}
	}ll ans=0,res=0;
	fd(i,(1<<top)-1,0){
		int val=0;
		fp(j,0,top-1)if(i>>j&1)val^=st[j];
		ll a=0,b=1;
		fp(j,0,K-1){
			a*=val,b*=val;
			a+=(b>>top),b&=(1<<top)-1;
		}ans+=a,res+=b;
		ans+=(res>>top),res&=(1<<top)-1;
	}printf("%llu",ans);
	if(res)puts(".5");
}
int main(){
//	freopen("testdata.in","r",stdin);
	n=read(),K=read();
	fp(i,0,n-1)a[i]=read();
	if(K==1)solve1();
	else if(K==2)solve2();
	else solve3();
	return 0;
}
原文地址:https://www.cnblogs.com/bztMinamoto/p/10243316.html