UOJ #36「清華集訓2014」瑪裡苟斯
阿新 • • 發佈:2018-12-04
這怎麼想得到啊.........
題意:求隨機一個集合的子集的異或和的$k$次方的期望值,保證答案$ \lt 2^{63},1 \leq k \leq 5$
$ Solution:$
首先考慮$ k=1$的時候怎麼做:如果某位上有$ 1$則有$ \frac{1}{2}$的概率可以取到這一位
$ k=1$時每一位都是獨立的,可以直接做
然後考慮$ k=2$時怎麼做
如果一個集合中有元素$ a,b$,則產生的貢獻為$ a^2+2ab+b^2$
我們把$ a^2$和$2ab$分開討論
如果某位有$ 1$,則有$ \frac{1}{4}$的概率取到$ a^2$
如果某兩個不同的位均有$ 1$,則有$ \frac{1}{4}$的概率取到$ 2ab$
注意如果這兩個不同的位只能一起被取,這個概率將被改成$ \frac{1}{2}$
然後考慮$ 3 \leq k \leq 5$時怎麼做
發現產生任何一個能夠被原集合的若干個數異或和表示的數都是等概率的
因此我們只需要保留原集合的線性基即可
由於答案$ \lt 2^{63}$,能產生的最大的數並不大,大約為$ \sqrt[k]{2^{63}}$級別
因此能表示出的數的數量大致也是這個級別的
建出線性基之後爆搜每個數並統計答案
注意中間計算過程中可能會爆$ long \ long$可能需要手寫壓位或$int128$
$ my \ code(int128)$
#include<ctime> #include<cmath> #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> #include<queue> #define rt register int #define ll long long using namespace std; inline ll read(){ ll x= 0; char zf = 1; char ch = getchar(); while (ch != '-' && !isdigit(ch)) ch = getchar(); if (ch == '-') zf = -1, ch = getchar(); while (isdigit(ch)) x = x * 10 + ch - '0', ch = getchar(); return x * zf; } void write(ll y){if(y<0)putchar('-'),y=-y;if(y>9)write(y/10);putchar(y%10+48);} void writeln(const ll y){write(y);putchar('\n');} int i,j,k,m,n,x,y,z;unsigned ll cnt,c[65],a[65]; namespace subtask1{ bool b[65]; void main(){ ll ans=0; for(rt i=1;i<=n;i++)for(rt j=0;j<=63;j++)if(a[i]>>j&1)b[j]=1; for(rt j=0;j<=63;j++)if(b[j])cnt+=(unsigned ll)1<<j; cout<<(cnt/2);if(cnt%2)cout<<".5"; } } namespace subtask2{ bool b1[65][65],b2[65][65],b[65];//b1兩位都有,b2兩位都沒有 void main(){ __int128 ans=0; for(rt i=1;i<=n;i++) for(rt j=0;j<=63;j++) for(rt k=0;k<=63;k++)if(j!=k){ if(a[i]>>j&1){ if(a[i]>>k&1)b1[j][k]=1;else b2[j][k]=1; b[j]=1; } } for(rt i=0;i<=63;i++) for(rt j=i+1;j<=63;j++){ __int128 all=0; if(b1[i][j])all=2; if(b1[i][j]&&(b2[i][j]||b2[j][i]))all=1; if(b2[i][j]&&b2[j][i])all=1; if(all) ans+=all*2*(1ll<<i)*(1ll<<j); } for(rt i=0;i<=63;i++)if(b[i])ans+=(unsigned ll)2*(1ll<<i)*(1ll<<i); ans/=2;write(ans/2); if(ans&1)cout<<".5"; } } namespace subtask3{ __int128 ans; __int128 mi(__int128 x,int y){ __int128 ans=1; for(rt i=1;i<=y;i++)ans*=x; return ans; } void dfs(int x,__int128 y){ if(x>n){ ans+=mi(y,m); return; } dfs(x+1,y);dfs(x+1,y^a[x]); } void main(){ ans=0;dfs(1,0); for(rt i=1;i<n;i++)ans/=2; write(ans/2);if(ans&1)cout<<".5"; } } int main(){ n=read();m=read(); for(rt i=1;i<=n;i++){ ll x=read(); for(rt j=63;j>=0;j--)if(x>>j&1){ if(c[j])x^=c[j]; else { c[j]=x; break; } } } n=0; for(rt i=0;i<=63;i++)if(c[i])a[++n]=c[i]; if(m==1)subtask1::main(); if(m==2)subtask2::main(); if(m>=3)subtask3::main(); return 0; }