題解 - 牛客H Playing games (想法+Fwt)
題目鏈接:https://ac.nowcoder.com/acm/contest/146/H
題意
給出N個數字,現在要求取出最少的數字,使其數組剩下的值異或和爲0
數據範圍:
思路
根據線性基的思想,我們不難想到,最多取出19個數字,一定能夠使得剩下數字異或和爲零。
這個我們可以直接從1掃到19,或者二分取出個數字。
那麼我們現在的問題就是,怎麼判斷取出了個數字後其可以組成異或的值。
令代表,組成異或值爲i的方案數,那麼不難得到,我們需要求的是
這個式子,我們要求次,那麼我們只需要將其FWT一下,再將其得到的,即可。
要檢驗時,就在將其UFWT變成正常的樣子,看其是否有值
代碼
#include <bits/stdc++.h>
using namespace std;
#define rep(i,j,k) for(ll i = (ll)j;i <= (ll)k;i ++)
#define debug(x) cerr<<#x<<":"<<x<<endl
#define pb push_back
typedef long long ll;
typedef pair<ll,ll> pi;
const ll MAXN = (ll)1e6+7;
const ll MOD = (ll)1e9+7,inv2 = (MOD+1)/2;
ll fpow(ll a,ll x) {
ll res = 1;
while (x) {
if (x&1) res = 1LL*res*a%MOD;
x >>= 1;
a = 1LL*a*a%MOD;
}
return res;
}
void fwtxor(ll f[],ll n,ll op){
for(ll p=2;p<=n;p<<=1){
ll len=p>>1;
for(ll k=0;k<n;k+=p)
for(ll i=k;i<k+len;++i){
ll t=f[i+len];
f[i+len]=(f[i]-f[i+len]+MOD)%MOD;
f[i]=(f[i]+t)%MOD;
}
if(op==-1) for(ll i=0;i<n;++i) f[i]=(ll)f[i]*inv2%MOD;
}
}
ll f[MAXN],a[MAXN];
ll N,sum,num=1<<19;
bool OK(ll m) {
rep(i,0,num-1) {
f[i] = fpow(a[i],m);
}
fwtxor(f,num,-1);
f[sum] = (f[sum]+MOD)%MOD;
return f[sum]>0;
}
int main()
{
scanf("%lld",&N);
rep(i,1,N) {
ll tmp;
scanf("%lld",&tmp);
a[tmp] ++;
sum ^= tmp;
}
a[0] ++;
fwtxor(a,num,1);
ll l = 0,r = 19;
while (l <= r) {
ll m = l+r>>1;
if (OK(m)) r = m-1;
else l = m+1;
}
ll ans = N-l;
printf("%lld\n",ans);
}