[清华集训2014]玛里苟斯

本文探讨了一种针对63位数的复杂概率计算方法,通过分层讨论不同情况下数值的贡献,包括单个数值、成对数值以及更复杂组合的情况。通过线性基和深度搜索策略,解决了高维空间中的概率估算问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、题目

点此看题

二、解法

注意到答案是 2 63 2^{63} 263次方,可以分 k k k来讨论:

k = 1 k=1 k=1,求出所有值或起来的值,每一位有 1 2 \frac{1}{2} 21的概率有贡献,所以把这个值除以 2 2 2即可。

k = 2 k=2 k=2,枚举 i , j i,j i,j,考虑贡献是 2 i + j × p 2^{i+j}\times p 2i+j×p p p p i , j i,j i,j同时有值的概率,考虑 a a a中0有没有 10 10 10 01 01 01(是 i , j i,j i,j位上有没有值),如果有的话 p p p 0.25 0.25 0.25,否则 p p p 0.5 0.5 0.5,那么 0.25 0.25 0.25是怎么得出来的呢?分成三种情况,只有 01 01 01,只有 10 10 10,有 01 01 01 10 10 10,计算就交给读者了,限于篇幅不给出详细说明。

k ≥ 3 k\geq3 k3,我们建出线性基,然后直接爆搜即可(想一想,每种情况的概率是 2 − c n t 2^{-cnt} 2cnt c n t cnt cnt是线性基里的个数,因为每种情况的个数是 2 n − c n t 2^{n-cnt} 2ncnt),只用搜O( 2 63 / 3 2^{63/3} 263/3)那么次即可。

详细实现中还有一些细节,可以康康我的代码和注释。

#include <cstdio>
#define int unsigned long long
const int M = 100005;
int read()
{	
	int x=0,flag=1;char c;
	while((c=getchar())<'0' || c>'9') if(c=='-') flag=-1;
	while(c>='0' && c<='9') x=(x<<3)+(x<<1)+(c^48),c=getchar();
	return x*flag;
}
int n,m,k,sum,tot=1,a[M],p[70],ans,ret;
void fuck1()
{
	for(int i=1;i<=n;i++)
		ans|=a[i];
	printf("%lld",(long long)(ans/2));
	(ans%2)?puts(".5"):puts("");
}
void fuck2()
{
	for(int i=1;i<=n;i++)
		sum|=a[i];
	for(int i=0;i<32;i++)
		for(int j=0;j<32;j++)
			if((sum>>i&1) && (sum>>j&1))
			{
				int f=0;
				for(int k=1;k<=n;k++)
					if((a[k]>>i&1)^(a[k]>>j&1)) {f=1;break;}
				if(i+j<1+f) ret++;//保证不能有负数哟 
				else ans+=(1ll<<(i+j-1-f));
			}
	printf("%lld",(long long)(ans+ret/2));
	(ret%2)?puts(".5"):puts("");
}
void ins(int x)
{
	for(int i=m;~i;i--)
	//无符号减到-1会炸 
	{
		if(!(x>>i&1))continue;
		if(!p[i]) {tot<<=1;p[i]=x;break;}
		x^=p[i];
	}
}
void dfs(int x,int y)
{
	if(x>m)
	{
		int r=1;
		for(int i=1;i<k;i++) r=r*y;
		ans+=r/tot*y;r%=tot;r*=y;ret+=r;
		ans+=ret/tot;ret%=tot;
		//要这样来卡精度 
		return ;
	}
	if(p[x]) dfs(x+1,y),dfs(x+1,y^p[x]);
	else dfs(x+1,y);
}
void fuck3()
{
	if(k==3) m=21;
	if(k==4) m=16;
	if(k==5) m=13;
	for(int i=1;i<=n;i++)
		ins(a[i]);
	dfs(0,0);
	printf("%lld",(long long)ans);
	ret?puts(".5"):puts("");
}
signed main()
{
	n=read();k=read();
	for(int i=1;i<=n;i++)
		a[i]=read();
	if(k==1) fuck1();
	if(k==2) fuck2();
	if(k>=3) fuck3();
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值