題解 - 牛客多校第八場 H Playing games (想法+Fwt)

題解 - 牛客H Playing games (想法+Fwt)

題目鏈接:https://ac.nowcoder.com/acm/contest/146/H

題意

給出N個數字,現在要求取出最少的數字,使其數組剩下的值異或和爲0

數據範圍:1N,Val51051\le N,Val \le 5*10^5


思路

根據線性基的思想,我們不難想到,最多取出19個數字,一定能夠使得剩下數字異或和爲零。

這個我們可以直接從1掃到19,或者二分取出MM個數字。

那麼我們現在的問題就是,怎麼判斷取出了MM個數字後其可以組成異或的值。

F[i]F[i]代表,組成異或值爲i的方案數,那麼不難得到,我們需要求的是

F[i]=jk=i(F[j]+F[k])F[i] = \sum_{j\land k= i} (F[j]+F[k])

這個式子,我們要求MM次,那麼我們只需要將其FWT一下,再將其得到的fpow(F,M)fpow(F',M),即可。

要檢驗時,就在將其UFWT變成正常的樣子,看其F[sum]F[sum]是否有值


代碼

#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);
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章