【bzoj3811】【清华集训2014】玛里苟斯
3811: 玛里苟斯
Time Limit: 10 Sec Memory Limit: 256 MBSubmit: 500 Solved: 196
[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
0
1
2
3
Sample Output
3.5
HINT
限制与约定
1≤n≤100000,1≤k≤5,ai≥0。最终答案小于 2^63 。k=1,2,3,4,5 各自占用 20% 的数据
Source
题解:
wwwwodddd ORZ
求子集异或和k次方的期望;
首先这和期望的k次方不一样,所以还是老老实实按k分类讨论,按位算贡献吧:
k=1 , 考虑第i位是否有1,有会贡献的$2^{i-1} $, 全部或起来除二;
k=2,如果某个异或和的第i位和第j为都有值,会贡献$2^{i+j}$的答案 , 首先这两位都必须要有至少一个1;
紧接着如果对于每一个数来说,这两位的值都相同 ,说明两位不相互独立,所以概率是1/2,期望是$2^{i+j-1}$;
否则说明两位独立,在异或运算下(0,0)(0,1)(1,0)(1,1)的概率相同为1/4,期望是$2^{i+j-2}$;
k>=3 , 由于答案在2^63次方以内,所以线性基的大小不会超过22,直接暴力枚举计算期望;
这题有一个结论是答案*2一定是整数;
也就是答案的小数最多有一位;
这里有个评论证明了,但是我没太看懂: https://blog.sengxian.com/solutions/bzoj-3811 自己给出一个可能不太严谨的证明吧(没学过数学。。。): 可以仔细分析一下k==2时的算法; 再扩展到k次方,发现在异或运算下: 二进制位之间贡献不相互独立是具有传递性的; 假设一次计算答案时选定的k个二进制位(可能相同分)集合为: B = {b1,b2,...bk} 我们可以把他们进一步分成m个集合: S1...Sm 相同集合元素贡献不互相独立,不同集合贡献互相独立; 这时对答案期望的贡献应该是2^{b1+b2+...+bk - m} ; 而k >= m , 且B里面至少有m个不同的二进制位(即bi!=bj这种); 所以考虑b1+b2+...+bk - m最小的情况: 分析可以发现最小为-1; 所以答案小数点后只有一位; 。。。。。。
这样就可以用一个数存下对2^|B|的除数和余数,分类讨论小数位的情况(建议看下代码);
1 #include<cstdio> 2 #include<iostream> 3 #include<algorithm> 4 #include<cstring> 5 #include<queue> 6 #include<cmath> 7 #include<vector> 8 #include<stack> 9 #include<map> 10 #include<set> 11 #define Run(i,l,r) for(int i=l;i<=r;i++) 12 #define Don(i,l,r) for(int i=l;i>=r;i--) 13 #define ll unsigned long long 14 #define ld long double 15 #define inf 0x3f3f3f3f 16 using namespace std; 17 const int N=100010; 18 int n,m,cnt; 19 ll a[N],d[100],res,ans; 20 char gc(){ 21 static char*p1,*p2,s[1000000]; 22 if(p1==p2)p2=(p1=s)+fread(s,1,1000000,stdin); 23 return(p1==p2)?EOF:*p1++; 24 } 25 ll rd(){ 26 ll x=0; char c=gc(); 27 while(c<'0'||c>'9')c=gc(); 28 while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+c-'0',c=gc(); 29 return x; 30 } 31 void solve1(){ 32 Run(i,1,n){ 33 ll x=rd(); 34 ans|=x; 35 } 36 printf("%llu",ans>>1); 37 if(ans&1)printf(".5\n"); 38 else puts(""); 39 } 40 void solve2(){ 41 Run(i,1,n)a[i]=rd(); 42 for(int i=0;i<=32;i++) 43 for(int j=0;j<=32;j++){ 44 int fg1=0,fg2=0,fg3=0; 45 for(int k=1;k<=n;k++){ 46 if(a[k]>>i&1)fg1=1; 47 if(a[k]>>j&1)fg2=1; 48 if((a[k]>>i&1)!=(a[k]>>j&1))fg3=1; 49 if(fg1&&fg2&&fg3)break; 50 } 51 if(!fg1||!fg2)continue; 52 if(i+j-fg3-1<0)res++; 53 else ans+=1ull<<(i+j-fg3-1); 54 } 55 ans+=res>>1; res&=1; 56 printf("%llu",ans); 57 if(res)printf(".5\n"); 58 else puts(""); 59 } 60 void solve3(){ 61 for(int i=1;i<=n;i++){ 62 ll x=rd(); 63 for(int j=63;~j;j--)if(x>>j&1){ 64 if(!d[j]){d[j]=x;break;} 65 else x^=d[j]; 66 } 67 } 68 for(int i=0;i<=63;i++)if(d[i])a[cnt++]=d[i]; 69 for(int i=0;i<1<<cnt;i++){ 70 ll x=0; 71 for(int j=0;j<cnt;j++)if(i>>j&1)x^=a[j]; 72 ll t1=0,t2=1; 73 for(int j=1;j<=m;j++){ 74 t1*=x , t2*=x; 75 t1 += t2 >> cnt , t2 &= (1<<cnt) - 1; 76 } 77 ans += t1 , res += t2; 78 ans += res >> cnt , res &= (1<<cnt) - 1; 79 } 80 printf("%llu",ans); 81 if(res)printf(".5\n"); 82 else printf("\n"); 83 } 84 int main(){ 85 freopen("in.in","r",stdin); 86 freopen("out.out","w",stdout); 87 n=rd(); m=rd(); 88 if(m==1)solve1(); 89 else if(m==2)solve2(); 90 else solve3(); 91 return 0; 92 }//by tkys_Austin;