第 k 大區間

青煙繞指柔!發表於2020-11-26

題目連結:第 k 大區間


因為k很大,顯然可以想到二分。利用字首異或和變成兩個點的異或。然後就是列舉每一個字尾,求異或大於某個數的個數。

直接Trie上查詢即可。


AC程式碼:

#pragma GCC optimize("-Ofast","-funroll-all-loops")
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e5+10;
int n,k,a[N],ch[N*32][2],sz[N*32],idx;
inline void insert(int x){
	int p=0;
	for(int i=30;i>=0;i--){
		int k=x>>i&1;
		if(!ch[p][k]) ch[p][k]=++idx;
		p=ch[p][k]; sz[p]++;
	}
}
inline int ask(int x,int mid){
	int p=0,s=0,cnt=0;
	for(int i=30;i>=0;i--){
		int k=x>>i&1;
		if(s+(1<<i)>=mid) cnt+=sz[ch[p][k^1]],p=ch[p][k];
		else s+=(1<<i),p=ch[p][k^1];
		if(!p) return cnt;
	}
	return cnt;
}
inline int check(int mid){
	int sum=0,s=0;
	memset(ch,0,sizeof ch),memset(sz,0,sizeof sz),idx=0;
	insert(0);
	for(int i=1;i<=n;i++){
		s^=a[i],sum+=ask(s,mid);
		insert(s);
	}
	return sum>=k;
}
signed main(){
	cin>>n>>k;
	for(int i=1;i<=n;i++) cin>>a[i];
	int l=0,r=4e9;
	while(l<r){
		int mid=l+r+1>>1;
		if(check(mid)) l=mid;
		else r=mid-1;
	}
	cout<<l;
	return 0;
}

相關文章