牛客练习赛112 B qsgg and Subarray

发布时间 2023-06-28 20:27:09作者: 突破铁皮

这里介绍两种解法,贪心和二分

核心:只要某一个区间和为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();
}