1. 程式人生 > >codeforces 1058E (思維)

codeforces 1058E (思維)

傳送

題意:給n個數,每個數的二進位制1都可以隨意的換位置,問區間異或為0的有多少個

思路:該問題可以轉化為他的充要條件:

1.區間1的個數為偶數

2.區間最大值不超過區間和的一半;

偶數區間個數可以用cnt[i][2]記錄以1開始,1~i中偶數和奇數區間個數,

那麼偶數區間就是假如現在字首是偶數,那就加cnt偶數,反之加奇數。

因為偶數=偶-偶/奇減奇

對於不符合的,因為數是60位左右,所以最大值不超過60,假如有一個數是60那麼其他數至少為1,這樣往前 遍歷60來次

就足矣。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=3e5+10;
int cnt[N][2], num[N];

int main(){
    int n;
    //cout<<__builtin_popcount(1000000000000ll)<<endl;
    scanf("%d", &n);

    for(int i=1; i<=n; i++){
        ll t;
        scanf("%I64d", &t);
        //num[i]=__builtin_popcount(t);
        while(t){
            if(t&1) num[i]++;
            t>>=1;
        }
        //printf("%d ", num[i]);
    }
    //puts("");
    cnt[0][0]=1;
    ll ans=0, sum=0;
    for(int i=1; i<=n; i++){
        int s=0, mx=0;
        sum+=num[i];
        if(sum&1)
            ans+=cnt[i-1][1];
        else
            ans+=cnt[i-1][0];
        cnt[i][1]=cnt[i-1][1]+(sum&1);
        cnt[i][0]=cnt[i-1][0]+(sum%2==0);
        for(int j=i; j>0&&i-j+1<=63; j--){
            s+=num[j];
            mx=max(mx, num[j]);
            if(s%2==0 && mx>s-mx) ans--;
        }
    }
    printf("%I64d\n", ans);

    return 0;
}