UOJ#36. 【清华集训2014】玛里苟斯 线性基
原文链接https://www.cnblogs.com/zhouzhendong/p/UOJ36.html
题解
按照 $k$ 分类讨论:
k=1 : 我们考虑每一位的贡献。若有至少一个数第 $i$ 位为 $1$ ,则对答案的贡献为 $2^i/2$ 。
k=2 : 发现每个异或和的平方为 $\sum_i\sum_j2^{i+j}bit_ibit_j$。那么考虑第 $i$ 位和第 $j$ 位的积的期望值。如果所有的数中,第 $i$ 位和第 $j$ 位均相等且非全零,那么参考 k=1 的情况,期望为 1/2;否则,第 $i$ 位为 $1$ 的概率为 1/2,第 $j$ 位为 $1$ 的概率为 1/2,$i×j$ 为 $1$ 的概率为 0.25 。
$k\leq 3$ : 由于答案不超过 $2^{63}$ ,直接把线性基搞出来之后暴力枚举就好了。
代码
#include <bits/stdc++.h> #define clr(x) memset(x,0,sizeof (x)) using namespace std; typedef long long LL; typedef unsigned long long ULL; LL read(){ LL x=0,f=0; char ch=getchar(); while (!isdigit(ch)) f|=ch=='-',ch=getchar(); while (isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=getchar(); return f?-x:x; } const int N=100005; int n,k; ULL a[N]; void init(){ static ULL x[64]; clr(x); n=read(),k=read(); for (int i=1;i<=n;i++){ ULL v=read(); for (int i=63;~i;i--) if (v>>i&1ULL) if (!x[i]){ x[i]=v; break; } else v^=x[i]; } n=0; for (int i=63;~i;i--) if (x[i]) a[++n]=x[i]; } void Out(ULL x){ cout<<x/2; if (x&1LLU) cout<<".5"; } namespace k1{ void solve(){ ULL ans=0; for (int i=1;i<=n;i++) ans|=a[i]; Out(ans); } } namespace k2{ void solve(){ ULL ans=0; for (int i=0;i<33;i++) for (int j=0;j<33;j++){ int f1=0,f2=0,f=0; for (int t=1;t<=n;t++){ f1|=a[t]>>i&1ULL; f2|=a[t]>>j&1ULL; f|=(a[t]>>i&1ULL)!=(a[t]>>j&1ULL); } if (!f1||!f2) continue; ans+=1ULL<<(i+j-f); } Out(ans); } } namespace k3{ __int128 tot; __int128 Pow(__int128 x,int y){ __int128 ans=1; for (;y;y>>=1,x*=x) if (y&1) ans*=x; return ans; } void solve(){ tot=0; for (int i=(1<<n)-1;i>=0;i--){ ULL tmp=0; for (int j=0;j<n;j++) if (i>>j&1) tmp^=a[j+1]; tot+=Pow(tmp,k); } while (tot%2==0&&n>1) n--,tot/=2; cout<<(ULL)tot/2; if (tot%2==1) cout<<".5"; } } int main(){ init(); if (k==1) k1::solve(); else if (k==2) k2::solve(); else k3::solve(); return 0; }