题目
题目概要
对于可重集合 ,设其元素个数为 ,显然它有 个子集。定义集合的权值为其中元素的异或和,求子集的权值的 次方的期望。即: 的值, 是集合内元素的异或和。
数据范围与约定
。
思路
刚看一眼,就被这道题给吓住了, 很大, 却小的可怜, 站在 旁边,显得毫不起眼。但我们不着眼于出题人的区别对待,我们要拯救每一个 ,让世界重新充满爱。
- 的时候,这道题变得平淡无奇,味同嚼蜡。所有的 中,有一半都满足第 位是一个 ,只要 中存在一个数字可以提供这个 。这是因为,这个数字是否出现,这件事不影响别人,它只负责自己的选择,就像 默默的 。若夫它看到是 ,它便义无反顾地将自己融入整个群体;至若此值已经为 ,它不会出现在人们的视野中,好像从未出现过,又好像一直都在。
- 的时候,我们要做的是一个平方的选择。两个元素,虽然不在一个括号中,但是二人的心却在一起,这就是平方的真谛。二者何人?枚举则知。不是所有人都能够相遇,就像人生总会有很多遗憾。如果二者可以被独立抉择——存在一个数字,在第 位为 而第 位为 ——那么二人是不容易遇到的,只有 。如果不是独立抉择,就会翻倍,得到 了。
- 的时候,我们终于可以大展拳脚了。我们有很多方法。我们可以利用答案小于 的特点,直接猜到 次方,求出线性基,然后暴力枚举。我们也可以继续学习 的方案。
我改悔了。
对于 ,我们拆位,将一个数字拆成很多个 的幂之和。然后平方就是选两个二进制位呗。
假设最大的一个二进制位是 ,那么至少有一半的异或和不小于 ,与 的情况类似。
所以答案至少有 ,可以解出 ,然后就没了。
作乘法可能会爆 ,稍微打的骚一点就好了。
代码
// 湘妹儿,永远滴神!
#include <cstdio>
#include <iostream>
#include <vector>
using namespace std;
typedef unsigned long long int_;
int_ readint(){
int_ x; scanf("%llu",&x);
return x; // cin就能兼容了[滑稽]
}
int_ d[100];
void insert(int_ x){
for(int i=63; i>=0&&x; --i)
if(x>>i&1){
if(d[i] == 0)
d[i] = x;
x ^= d[i];
}
}
int n, k, cnt; // 分母是pow(2,cnt)
int_ zheng, xiao; // 整数与小数
void dfs(int t,int_ now){
if(t == -1){
int_ res = 1; // 加入res*now
for(int i=1; i<k; ++i)
res *= now;
int_ yu = res%(1<<cnt);
res = res/(1<<cnt);
// 变成了res*(1<<cnt)+yu
zheng += res*now;
xiao += yu*now;
zheng += xiao/(1<<cnt);
xiao = xiao%(1<<cnt);
return ;
}
if(d[t]) dfs(t-1,now);
dfs(t-1,now^d[t]);
}
int main(){
n = readint(), k = readint();
for(int i=0; i<n; ++i)
insert(readint());
int_ all = 0;
for(int i=63; i>=0; --i)
all |= d[i];
if(k == 1){
cnt = 1;
zheng = all>>1;
xiao = all&1;
}
if(k == 2){
cnt = 2;
for(int i=32; i>=0; --i)
if(all>>i&1)
for(int j=32; j>=0; --j)
if(all>>j&1){
bool apart = false;
for(int o=32; o>=0; --o)
if((d[o]>>i&1)^(d[o]>>j&1))
{ apart = true; break; }
int mom = (apart ? 1 : 0)+1;
if(i+j >= mom)
zheng += (1ull<<(i+j-mom));
else
xiao += 1ull<<(i+j+cnt-mom);
// 为了让分母pow(2,mom->cnt)
zheng += xiao/(1<<cnt);
xiao = xiao%(1<<cnt);
}
}
if(k >= 3){
for(int i=0; i<=63; ++i)
if(d[i] > 0) ++ cnt;
dfs(63,0);
}
printf("%llu",zheng);
if(xiao) putchar('.');
while(xiao *= 10){
printf("%llu",xiao/(1<<cnt));
xiao %= (1<<cnt);
}
return 0;
}