[清华集训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);
}

 

posted @ 2018-10-06 19:31  _雨后阳光  阅读(196)  评论(1编辑  收藏  举报