codeforces 1030E (暴力+思維)

題目網址http://codeforces.com/problemset/problem/1030/E

題意:

給定一些數,可將區間 l ~r 中某些數的二進制位的1的位置更換, 使得最終區間所有數異或和爲0,求這樣的區間個數。

思路

在那裏瞎dp了好久,wa的很徹底,借鑑了一下別人的思路。

區間合法的條件是:

這個區間1的個數爲偶數,並且區間中二進制位1最多的一個數的二進制個數小於等於和的一半。

我們發現一個數至少可以增加一個二進制位,1e18,大約在2^61次方。那麼區間長度大於63時,一定可以把數字相互抵消成0,(即第二個條件一定滿足)。所以我們只需統計有多少個偶數區間即可。

對於區間長度小於63的,我們直接暴力枚舉即可。

代碼如下:

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <string>
#include <vector>
#include <map>
#include <queue>

using namespace std;
const int maxn = 3e5+100;
long long a[maxn];
long long get(long long n)
{
    long long num = 0;
    while(n>0)
    {
       if(n%2!=0)
        num++;
        n/=2;
    }
    return num;
}
long long numj[maxn];
long long numo[maxn];
long long all[maxn];
int main()
{
    int n;
    scanf("%d",&n);
    for(int i=1; i<=n; i++)
        scanf("%lld",&a[i]);
    long long ans = 0;
    all[0] = 0;
    numo[0] = 1;
    for(int i=1; i<=n; i++)
    {
        a[i] = get(a[i]); //剪枝操作
        all[i] = all[i-1] + a[i];
        long long mx = a[i];
        for(int j=i-1; j>=max(1,i-62); j--)
        {
            mx = max(mx,a[j]); //如果每次求一個get(a[j]) 會TE,所以直接保存就好
            long long len = all[i] - all[j-1];
            if((len%2==0) && (len>=mx*2))
                ans++;
        }
        if(i>63)
        {
            if(all[i]&1)
                ans+= numj[i-64];
            else
                ans+= numo[i-64];
        }
        numj[i] = numj[i-1] + (all[i]%2==1);
        numo[i] = numo[i-1] + (all[i]%2==0);
    }
    printf("%lld\n",ans);
     return 0;
}

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章