题意略。
思路:为了更好地求出一段连续数字的异或和,我们可以用前缀异或和来维护,现在我们只需要考虑每一个在数组中的数字向前异或,且在指定范围内,
异或值为全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; }