CSU1784

题意略。

思路:为了更好地求出一段连续数字的异或和,我们可以用前缀异或和来维护,现在我们只需要考虑每一个在数组中的数字向前异或,且在指定范围内,

异或值为全1的个数有多少个。算出每一个位子能做出的贡献,最后相加就可以了。

比如说现在的前缀xorsum = 1010,我们只需要知道在当前位置之前有多少个地方的xorsum值是0101,xor操作后,我们就可以得到这一段的异或值,

且长度要满足题意。

我们如果能记录下当前位子之前所有xorsum = 0101的下标就好了,最好还是有序的,这样就可以用二分查找来优化了。

这个可以用map<LL,vector<int> > 来做到。

我们一边计算,一边维护。

详见代码:

#include<iostream>
#include<algorithm>
#include<stdio.h>
#include<string.h>
#include<map>
#include<vector>
#define maxn 300005
using namespace std;
typedef long long LL;

map<LL,vector<int> > mp;
int n,m,a,b;
char str[55];
LL store[maxn];
const LL one = 1;

LL getans(LL tar,int idx){
    vector<int>& v = mp[tar];
    int up = idx - a;
    int down = idx - b;
    down = lower_bound(v.begin(),v.end(),down) - v.begin();
    up = upper_bound(v.begin(),v.end(),up) - v.begin();
    return LL(up - down);
}

int main(){
    int cas = 1;
    while(scanf("%d%d%d%d",&n,&m,&a,&b) == 4){
        mp.clear();
        for(int i = 0;i < m;++i){
            scanf("%s",str);
            LL s = 0;
            for(int j = n - 1,k = 0;j >= 0;--j,++k){
                s += LL(str[j] - '0')<<(LL)k;
            }
            store[i + 1] = s;
        }
        LL total = (one<<n) - one;
        LL sum = 0;
        LL ans = 0;
        mp[sum].push_back(0);
        for(int i = 1;i <= m;++i){
            sum = sum ^ store[i];
            LL other = total ^ sum;
            ans += getans(other,i);
            mp[sum].push_back(i);
        }
        printf("Case %d: %lld
",cas++,ans);
    }
    return 0;
}
原文地址:https://www.cnblogs.com/tiberius/p/9298040.html