[清华集训2015 Day1]玛里苟斯-[线性基]
Description
Solution
考虑k=1的情况。假设所有数中,第i位为1的数的个数为x,则最后所有的子集异或结果中,第i位为1的个数为$(C_{k}^{1}+C_{k}^{3}+...)$*2原本的数中第i位为0的数的个数。同理,所有子集异或结果中第i位为0的个数为$(C_{k}^{0}+C_{k}^{2}+...)$*2原本的数中第i位为0的数的个数。
由于二项式定理,可得前后两个式子大小相等。即对于每一位i,如果该位有某个(些)数为1,ans+=10i-1/2。
k=2同理。
对于k>2,我们发现假如某个数能够由其他若干个数异或而得,那么把这个数删掉对答案没有影响。可以用线性基。
Code
#include<iostream> #include<cstdio> #include<cstring> #include<cmath> using namespace std; const int N=1e5+10; typedef unsigned long long ull; int n,k,R;ull a[N]; ull b[30];int cnt; ull _ans,_res; void dfs(int x,ull c) { if (x==cnt+1) { ull num=0,yu=1; for (int i=1;i<=k;i++) { num*=c;yu*=c; num+=yu>>cnt;yu&=R; } _res+=yu; _ans+=num+(_res>>cnt); _res&=R; return; } dfs(x+1,c); dfs(x+1,c^b[x]); } int main() { scanf("%d%d",&n,&k); for (int i=1;i<=n;i++) scanf("%llu",&a[i]); if (k==1) { ull t=0; for (int i=1;i<=n;i++) t|=a[i]; printf("%llu%s",t>>1,t&1?".5":0);return 0; } if (k==2) { ull t=0,ans=0; for (int i=1;i<=n;i++) t|=a[i]; for (int i=0;i<32;i++) for (int j=i;j<32;j++) if (t>>i&&t>>j) ans+=1ull<<(i+j); printf("%llu%s",ans>>1,ans&1?".5":0);return 0; } ull t[30]; memset(t,0,sizeof(t)); for (int i=1;i<=n;i++) for (int j=21;j>=0;j--) { if (a[i]&(1<<j)) if (!t[j]) {t[j]=a[i];break;} a[i]^=t[j]; } for (int i=0;i<=21;i++) if (t[i]) b[++cnt]=t[i]; R=(1<<cnt)-1; dfs(1,0); if (_res) printf("%llu.5",_ans);else printf("%llu",_ans); }