bzoj 3811: 玛里苟斯(期望+线性基)

3811: 玛里苟斯

Time Limit: 10 Sec   Memory Limit: 256 MB
Submit: 223   Solved: 98
[ Submit][ Status][ Discuss]

Description

魔法之龙玛里苟斯最近在为加基森拍卖师的削弱而感到伤心,于是他想了一道数学题。
S 是一个可重集合,S={a1,a2,…,an}。
等概率随机取 S 的一个子集 A={ai1,…,aim}。
计算出 A 中所有元素异或 x, 求 xk 的期望。

Input

第一行两个正整数 n, k。
以下 n 行每行一个整数,表示 ai。

Output

如果结果是整数,直接输出。如果结果是小数(显然这个小数是有限的),输出精确值(末尾不加多余的 0)。

Sample Input

4 2
0
1
2
3

Sample Output

3.5


比较难的题

求出n个数的线性基,线性基中每个集合的异或和都唯一,并且属于满射

所以可以暴力线性基的所有集合异或和,然后加在一起除以2^cnt就是答案了(cnt为线性基元素个数)

但是线性基中最多有62个数,暴力是不行的

考虑按k分情况讨论

①k>=3:

因为答案不会超过long long范围,所以线性基中所有元素不会超过64/3个,可以直接暴力

不过注意一个坑:答案不会超过long long但是你计算所有异或和最后要除以2^cnt才是答案,所以计算所有贡献的过程中可能会爆long long,要边计算边除,也就是将val写成val/(2^cnt)和val%(2^cnt)两部分;

②k = 1:

不能暴力了,假设某个数ai转成二进制后第k位为1,因为这个数被选的概率刚好是1/2,并且很明显,无论它和谁异或,这个1一定都会有刚好1/2的概率对答案有贡献,所以可以发现答案就是所有数or起来/2

③k = 2:

假设某个集合的异或和为x,x转成二进制后是bm…b1b2b3(bm表示二进制第m位,0/1),那么x²就等于∑bibj*2^(i+j),当且仅当第i位第j位都为1时,对答案计算有2^(i+j)的贡献,用上面k=1的思想可以轻松得出这个概率是1/4(有00, 10, 01, 11四种情况各1/4),不过,当所有数第i位和第j位都相同时,概率会变为1/2(只有00,11两种情况),当所有数第i位都为0或者第j位都为0时概率为0


#include<stdio.h>
#define LL unsigned long long
LL a[100005], p[66], jz[66], er[66] = {1};
int main(void)
{
	LL ans, temp, mod, c, d;
	int i, j, k, cnt, n, flag;
	scanf("%d%d", &n, &k);
	for(i=1;i<=62;i++)
		er[i] = er[i-1]*2;
	for(i=1;i<=n;i++)
		scanf("%llu", &a[i]);
	if(k==1)
	{
		ans = 0;
		for(i=1;i<=n;i++)
			ans |= a[i];
		printf("%llu", ans/2);
		if(ans%2)
			printf(".5");
	}
	else if(k==2)
	{
		temp = 0;
		for(i=1;i<=n;i++)
			temp |= a[i];
		mod = ans = 0;
		for(i=62;i>=0;i--)
		{
			for(j=62;j>=0;j--)
			{
				if((temp&(1ll<<i))==0 || (temp&(1ll<<j))==0)
					continue;
				flag = 1;
				for(k=1;k<=n;k++)
				{
					if((a[k]&(1ll<<i)) && (a[k]&(1ll<<j))==0 || (a[k]&(1ll<<i))==0 && (a[k]&(1ll<<j)))
					{
						flag = 0;
						break;
					}
				}
				if(flag)
				{
					ans += er[i+j]/2;
					mod += er[i+j]%2;
				}
				else
				{
					ans += er[i+j]/4;
					if(er[i+j]%4)
						mod++;
				}
			}
		}
		printf("%llu", ans+mod/2);
		if(mod%2)
			printf(".5");
	}
	else
	{
		cnt = 0;
		for(i=1;i<=n;i++)
		{
			for(j=62;j>=0;j--)
			{
				if(a[i]&(1ll<<j))
				{
					if(p[j]==0)
					{
						p[j] = a[i];
						jz[cnt++] = p[j];
						break;
					}
					else
						a[i] ^= p[j];
				}
			}
		}
		ans = mod = 0;
		for(i=0;i<(1ll<<cnt);i++)
		{
			temp = 0;
			for(j=0;j<=cnt-1;j++)
			{
				if(i&(1ll<<j))
					temp ^= jz[j];
			}
			c = 0, d = 1;
			for(j=1;j<=k;j++)
			{
				c *= temp, d *= temp;
				c += d/er[cnt], d %= er[cnt];
			}
			ans += c, mod += d;
			ans += mod/er[cnt], mod %= er[cnt];
		}
		printf("%llu", ans);
		if(mod)
			printf(".5");
	}
	printf("\n");
	return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值