【BZOJ3811】玛里苟斯(线性基)

【BZOJ3811】玛里苟斯(线性基)

题面

BZOJ

题解

\(K=1\)很容易吧,拆位考虑贡献,所有存在的位出现的概率都是\(0.5\),所以答案就是所有数或起来的结果除二。
\(K=2\)的情况,我们直接拆开平方式,平方项的贡献直接算,出现的概率还是\(0.5\),然后\(2ab\)这样子的东西出现的概率是\(0.5*0.5=0.25\),然而注意到有一些位直接两两之间存在联系,即选择了第\(i\)位的时候必定会同时选择第\(j\)位,那么此时两位之间的概率就是\(0.5\),这一部分要特殊判断一下。
接下来考虑\(K\ge 3\)的情况。因为答案不会超过\(2^{63}\),所以任何一个数字都不会从超过\(2^{22}\)。我们知道如果一个数可以被线性基中的其他所有数给表示出来,那么这个数出现的概率一定是\(1/{2^{|G|}}\),其中\(|G|\)是线性基内的元素个数。既然数字大小不大,那么线性基中的元素个数也不多,所以可以构建线性基之后直接\(2^{|G|}\)枚举所有可能出现的数字,\(K\)次方之后求和即可。
注意一下,最终答案除掉总的数字个数之后在\(2^{63}\)以内,那么在除之前是可能爆掉的,所以这里可以拆位什么的存一下就好了。

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<vector>
using namespace std;
#define ll long long
#define ull unsigned long long
inline ull read()
{
	ull x=0;bool t=false;char ch=getchar();
	while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
	if(ch=='-')t=true,ch=getchar();
	while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
	return t?-x:x;
}
int n,K;ull a[100100];
namespace Task1
{
	void Solve()
	{
		ull ans=0;
		for(int i=1;i<=n;++i)ans|=a[i];
		if(ans&1)printf("%llu.5\n",ans>>1);
		else printf("%llu\n",ans>>1);
	}
}
namespace Task2
{
	bool vis[50];
	bool check(int x,int y)
	{
		if(!vis[x]||!vis[y])return false;
		for(int i=1;i<=n;++i)
		{
			if((a[i]&(1ll<<x))&&!(a[i]&(1ll<<y)))return false;
			if((a[i]&(1ll<<y))&&!(a[i]&(1ll<<x)))return false;
		}
		return true;
	}
	void Solve()
	{
		ull ans=0;
		for(int i=0;i<33;++i)
			for(int j=1;j<=n;++j)
				if(a[j]&(1ll<<i)){vis[i]=true;break;}
		for(int i=0;i<33;++i)if(vis[i])ans+=1ll<<(i+i);
		for(int i=0;i<33;++i)
			for(int j=i+1;j<33;++j)
				if(vis[i]&&vis[j])ans+=1ll<<(i+j);
		for(int i=0;i<33;++i)
			for(int j=i+1;j<33;++j)
				if(check(i,j))ans+=1ll<<(i+j);
		if(ans&1)printf("%llu.5\n",ans>>1);
		else printf("%llu\n",ans>>1);
	}
}
namespace Task3
{
	int p[50];
	void insert(int x)
	{
		for(int i=30;~i;--i)
			if(x&(1<<i))
			{
				if(!p[i]){p[i]=x;return;}
				x^=p[i];
			}
	}
	int S[55],top;
	ull ans=0,r=0;
	void Calc(int val)
	{
		int MOD=1<<top;
		ull D=0,R=1;
		for(int i=1;i<=K;++i)
		{
			D*=val;R*=val;
			D+=R/MOD;R%=MOD;
		}
		ans+=D;r+=R;
		ans+=r/MOD;r%=MOD;
	}
	void dfs(int x,int val)
	{
		if(x==top+1){Calc(val);return;}
		dfs(x+1,val);dfs(x+1,val^S[x]);
	}
	void Solve()
	{
		for(int i=1;i<=n;++i)insert(a[i]);
		for(int i=0;i<=30;++i)if(p[i])S[++top]=p[i];
		dfs(1,0);
		if(r)printf("%llu.5\n",ans);
		else printf("%llu\n",ans);
		return;
	}
}
int main()
{
	n=read(),K=read();
	for(int i=1;i<=n;++i)a[i]=read();
	if(K==1){Task1::Solve();return 0;}
	if(K==2){Task2::Solve();return 0;}
	if(K>=3){Task3::Solve();return 0;}
	return 0;
}
posted @ 2018-12-03 08:23  小蒟蒻yyb  阅读(675)  评论(1编辑  收藏  举报