清华集训2014 玛里苟斯

  • 清华集训2014 玛里苟斯
  • 求子集异或和k次方的期望。
  • 异或考虑按位算贡献。
  • 对于\(K=1\),考虑异或和\(\frac{x}{2}\)就是答案。
  • 证明简单来说就是,你可以先打一个概率\(dp\)分别对每一位来考虑。
  • 假设当前考虑的位数是\(j\),如果\(i\)这个数是\(0\),那么选和不选的影响都是不改变原来的选取,如果\(i\)这个数是\(1\),那么选与不选是对称的,也就是把原来\(0\)\(1\)的概率相互交换。
  • 实际上我们在模拟一个或运算。
  • 对于\(K=2\),首先每一位如果有数,都会贡献\(4^j\)答案。
  • 如果异或和的第i位和第j为都有数,会贡献\(2^{i+j-1}\)的答案,因为同时出现的概率是\(\frac {1}{4}\)
  • 但是如果两位不是互相独立的,贡献的答案应该是\(2^{i+j}\),因为同时出现的概率是\(\frac {1}{2}\)
  • \(k>=3\),首先,线性基的定义就证明了,一个数集的异或子集,和他随便乱异或的数集的异或子集是等效的。
  • 所以可以线性基直接消掉线性相关的数。
  • 由于答案在\(2^{63}\)以内,所以线性基的大小不会超过\(22\),直接暴力枚举计算期望。
  • 这题有一个结论是答案两倍一定是整数,也就是答案的小数最多有一位;
  • \(k\leq3\)比较好证,但是\(k>3\)怎么证明啊……??
  • upd,找到证明
#include<bits/stdc++.h>
#define R register int
#define db long double
#define ll unsigned long long 
using namespace std;
const int N=200001;
int n,K;ll w[N];
ll gi(){
    ll x=0;R k=1;char c=getchar();
    while(c!='-'&&(c<'0'||c>'9'))c=getchar();
    if(c=='-')k=-1,c=getchar();
    while(c<='9'&&c>='0')x=(x<<3)+(x<<1)+c-'0',c=getchar();
    return x*k;
}
namespace cpp1{
	ll ans;
	void Main(){
		for(R i=1;i<=n;++i)ans|=w[i];
		printf("%lld",ans>>1ll);
		if(ans&1)cout<<".5";
	}
}
namespace cpp2{
	int hv[33];ll ans,res;
	int check(R p,R q){
		for(R i=1;i<=n;++i){
			if((w[i]&(1<<p))&&!(w[i]&(1<<q)))return 1;
			if((w[i]&(1<<q))&&!(w[i]&(1<<p)))return 1;
		}
		return 0;
	}
	void Main(){
		for(R i=1;i<=n;++i)res|=w[i];
		for(R i=0;i<33;++i){
			if((res&(1ll<<i))==0)continue;
			ans+=(1ll<<(i+i));
			for(R j=i+1;j<33;++j)
				if(res&(1ll<<j)){
					if(check(i,j))ans+=(1ll<<(i+j));
					else ans+=(2ll<<(i+j));
				}
		}
		printf("%lld",ans>>1);puts(ans&1?".5":"");
	}
}
namespace cpp3{
	int Mx,tot,len;ll res,num[300],now[300];__int128 ans;
	void ins(ll x){
		for(R j=0;j<Mx;++j){
			if((x&(1<<j))==0)continue;
			if(!num[j]){num[j]=x;return ;}
			x^=num[j];
		}
	}
	void Dfs(R i,ll nw){
		if(i==len+1){
			__int128 fin=1;
			for(R j=1;j<=K;++j)fin*=nw;
			ans+=fin,tot++;return ;
		}
		Dfs(i+1,nw),Dfs(i+1,nw^now[i]);
	}
	void Main(){
		for(R i=1;i<=n;++i){
			ll x=w[i];R nw=0;
			while(x)nw++,x>>=1;
			Mx=max(Mx,nw),res|=w[i];
		}
		for(R i=1;i<=n;++i)ins(w[i]);
		for(R i=0;i<Mx;++i)if(num[i])now[++len]=num[i];
		Dfs(1,0),ans>>=(len-1);ll res=ans>>1;
		printf("%lld",res),puts((ans&1)?".5":"");
	}
}
int main(){
	n=gi(),K=gi();
	for(R i=1;i<=n;++i)w[i]=gi();
	if(K==1)cpp1::Main();
	if(K==2)cpp2::Main();
	if(K>=3)cpp3::Main();
	return 0;
}


posted @ 2018-12-04 09:09  Tyher  阅读(266)  评论(0编辑  收藏  举报