18牛客多校8H

链接:https://www.nowcoder.com/acm/contest/146/H
来源:牛客网

题目描述
Niuniu likes playing games. He has n piles of stones. The i-th pile has ai stones. He wants to play with his good friend, UinUin. Niuniu can choose some piles out of the n piles. They will play with the chosen piles of stones. UinUin takes the first move. They take turns removing at least one stone from one chosen pile. The player who removes the last stone from the chosen piles wins the game. Niuniu wants to choose the maximum number of piles so that he can make sure he wins the game. Can you help Niuniu choose the piles?

输入描述:
The first line contains one integer n (1 ≤ n ≤ 500000), which means the number of piles.
The second line describes the piles, containing n non-negative integers, a1 a2 … an, separated by a space. The integers are less than or equal to 500000.

输出描述:
Print a single line with one number, which is the maximum number of piles Niuniu can choose to make sure he wins. If Niuniu cannot always win whatever piles he chooses, print 0.

题意:队友告诉我这题就是给你n个数,找出其中最多的数的异或和为0。
题解:设 sum s u m 为这n个数的异或和由于异或的性质,可以把此题转化为找出最少的数使得他们的异或和为sum。。 ai a i 最大为 219 2 19 ,可以拆成19维的向量,由于每一维只有方向没有长度,n维空间中最多有n个线性无关向量,所以最多取19个向量就可一组合成 sum s u m ,二分一下就可以。然后可以利用FWT加速。因为 Ci=AjBk(j C i = ∑ A j B k ( j ^ k==i) k == i )
然后就可以类似母函数的“挂衣架”的计数功能快速求出。

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const LL MOD=1e9+7;
const LL ny2=500000004;
const int N=1e6;
LL pod(LL x,LL n,LL MOD)
{
    LL ret=1;
    while(n)
    {
        if(n&1)ret=ret*x%MOD;
        x=x*x%MOD;
        n>>=1;
    }
    return ret;
}

void FWT(LL a[],int len,int ok)
{
    for(int k=1; k<len; k<<=1)
        for(int i=0; i<len; i+=(k<<1))
            for(int j=0; j<k; j++)
                if(ok)
                {
                    LL x=a[i+j],y=a[i+j+k];
                    //and:a[i+j]+=a[i+j+k];
                    //or :a[i+j+k]+=a[i+j];
                    //xor
                    a[i+j]=(x+y)%MOD;
                    a[i+j+k]=(x-y+MOD)%MOD;
                }
                else
                {
                    LL x=a[i+j],y=a[i+j+k];
                    //and:a[i+j]-=a[i+j+k];
                    //or :a[i+j+k]-=a[i+j];
                    //xor
                    a[i+j]=1ll*(x+y)*ny2%MOD;
                    a[i+j+k]=1ll*(x-y)*ny2%MOD;
                }
}
LL a[N],b[N];
int sum,len;
bool check(int x)
{
    for(int i=0;i<len;i++)
    {
        b[i]=pod(a[i],x,MOD);
    }
    FWT(b,len,0);
    return b[sum]!=0;
}
int main()
{
    int n,x;
    scanf("%d",&n);
    len=1<<19;
    sum=0;
    for(int i=0;i<n;i++)
    {
        scanf("%d",&x);
        sum^=x;
        a[x]=1;
    }
    a[0]=1;
    FWT(a,len,1);
    int l=0,r=20,mid;
    while(l<r)
    {
        mid=(l+r)>>1;
        if(check(mid))
            r=mid;
        else
            l=mid+1;
    }
    printf("%d\n",n-l);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值