1. 程式人生 > >codeforces|CF1054D Changing Array

codeforces|CF1054D Changing Array

因為資料範圍是2e5級別的,所以我們考慮用異或字首和來處理區間的異或情況。(比如說a包括b,那麼我們通過異或可以知道b對於a的補區間的資訊)

之後因為對任意\(a_i\)進行取反操作,會改變它和它之後的區間值(原來相等的都不相等了,原來不相等的都相等了),所以結果是我們對原先的字首和直接取反,就可以表示對它進行取反操作之後的字首和了。

因為不管怎麼取反操作,一個位置的字首和只有兩種,那麼我們顯然就可以預處理出所有的值了。

然後我們可以把相同類別的放在一個map裡面。(注意要避免重複,比如說010和101是一類)

之後把ans值\((==n*(n+1)/2)\)減去重複的就可以了。

因為是要求區間異或和不為0,那麼轉化下來的條件就是字首和選取的兩個不相等即可。

我們要求最大的個數,那麼就是同一類的最好均分,用組合數算就行了。(比如說有5個010的話,分成2個010和3個101就行了)

最後注意一下要先給0類加一。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<map>
#define MAXN 200010
using namespace std;
long long ans;
int pre[MAXN];
map<int,int>m;
int n,k,x;
int main() {
    scanf("%d%d",&n,&k);
    m[0]++;
    for(int i=1;i<=n;i++) 
    {
        scanf("%d",&x);
        pre[i]=pre[i-1]^x;
        pre[i]=min(pre[i],pre[i]^((1<<k)-1));
        m[pre[i]]++;
    }
    ans=(long long)(n+1)*n/2;
    for (map<int,int>::iterator it=m.begin();it!=m.end();it++) 
    {
        long long x=it->second;
        ans-=(long long)((x/2)-1)*(x/2)/2;
        ans-=(long long)((x-x/2)-1)*(x-x/2)/2;
    }
    printf("%lld\n",ans);
}