牛客练习赛112 B qsgg and Subarray
这里介绍两种解法,贪心和二分
核心:只要某一个区间和为0,则所有包含该区间的和都为0
贪心
根据题意是求出所有⊕和为0的子区间的个数,我们按a[i]来分类,每次求出以a[i]为末尾,区间和为0的区间个数,对于a[i]来说,要想u~i的区间和为0,则需要包含所有a[i]中位为1都有0与之对应,如果u~i的区间和为0的话则,所有包含该区间的区间和都为0,因此区间个数为u
#include <iostream>
#include <cstring>
#include <string>
#include <algorithm>
#include <cmath>
#include <set>
#include <utility>
#include <vector>
#include <queue>
#include <map>
using namespace std;
const int N=1e6+10;
int a[N],last[N];//last[i]记录的是第i位为0的最右边的下标
int n;
void solve(){
cin>>n;
for(int i=1;i<=n;i++){
cin>>a[i];
}
long long res=0;
for(int i=1;i<=n;i++){
int k=a[i];
int x=N;
for(int j=1;j<=32;j++){
if(k&1){
x=min(x,last[j]);
}
else last[j]=i;//及时更新下标为最靠右的,才能使得区间最小
k>>=1;
}
if(x==N) x=i;//如果x==N则说明a[i]==0,则区间个数为i
res+=x;
}
cout<<res;
}
int main(){
solve();
}
二分
我们发现只要子区间为0,父区间一定为0,因此我们可以二分求出,i~mid--1区间不为0,i~mid一直到i~n的区间都为0
#include <iostream>
#include <cstring>
#include <string>
#include <algorithm>
#include <cmath>
#include <set>
#include <utility>
#include <vector>
#include <queue>
#include <map>
using namespace std;
const int N=1e6+10,M=32;
int a[N],v[N][M];//v[i][j]记录前i个数字中第j位为1的个数
int n;
bool check(int l,int r){//如果对于每一位来说,必须至少存在某一个数字该位为0这样整个区间的区间和才会为0
for(int i=1;i<=32;i++){
if(v[r][i]-v[l][i]==r-l) return true;//如果个数和区间长度相等,说明存在某一位,该区间里所有的数字这一位都为1
}
return false;
}
void solve(){
cin>>n;
for(int i=1;i<=n;i++){
cin>>a[i];
int k=a[i];
for(int j=1;j<=32;j++){
v[i][j]=v[i-1][j];
if(k&1) v[i][j]++;
k>>=1;
}
}
long long res=0;
for(int i=1;i<=n;i++){
int l=i,r=n+1;
while(l<r){
int mid=l+r>>1;
if(check(i-1,mid)) l=mid+1;
else r=mid;
}
res+=n-l+1;//这里sum[i->l]~sum[i->n]的区间和都为0
}
cout<<res;
}
int main(){
solve();
}