分治两题(cdq分治、BIT)

本文介绍了两种使用BIT(树状数组)和CDQ分治算法解决区间查询问题的方法。在例题一中,通过BIT求解平均值大于等于k的区间个数,通过离散化和逆序数的概念简化问题。在例题二中,利用BIT计算区间和在[L,R]的区间个数。此外,还展示了如何用CDQ分治实现相同的目标。这些算法在处理大规模数据时具有高效性。
摘要由CSDN通过智能技术生成

例题一:求平均值大于等于k的区间个数

P2717 寒假作业

之前我也遇到这类题,当时数据范围比较小,n<=1e4,k<=100,甚至不用离散化,当时我的做法是BIT(树状数组)。
当时我是这么想的
1、遇到平均数,各项减去k,前缀和大于等于0的区间就符合题意。
2、从1遍历到n,到i时,每次找之后前缀和大于0的个数,就是以i为左端点时的贡献
3、对于第i个前缀和时,与第i-1位的前缀和相比,i-1之后所有数都减去a[i-1],所以转换为,前缀和不变情况下,每次找大于等于s[i-1]的数

现在我再次遇到这题,用离散化BIT也能过,后来我发现,一个区间[i,j]符合条件,s[j]-s[i-1]>=0,离散化后,各个数变成1到n之间,问题转换为找各个数之后比它大的数的个数。

这是什么?不就是求逆序数吗,难怪我刷cdq分治的题时,会感觉这题是BIT。

反过来想,这题如果用cdq分治,就类似归并排序的模板。

————BIT写法(就是一个加了离散化的求逆序数)、分治写法不写了

const ll inf=1e18+7;
const int maxn=2e5+7; 
ll n,m,a[maxn],t[maxn<<2],sum,k,s[maxn],c[maxn],cnt;
ll lowbit(ll i){	return i&(-i);	}
void add(ll i){	for(;i<=cnt;i+=lowbit(i))	t[i]++;	}
void sub(ll i){	for(;i<=cnt;i+=lowbit(i))	t[i]--;	}
ll query(ll i){
	int ans=0;
	for(;i;i-=lowbit(i))	ans+=t[i];
	return ans;
}
int main(){
	n=read();	m=read();
	for(int i=1;i<=n;i++){
		a[i]=read()-m;
		s[i]=s[i-1]+a[i];
		c[cnt++]=s[i];
	}
	c[cnt++]=inf;	c[cnt++]=0;
	sort(c,c+cnt);
	cnt=unique(c,c+cnt)-c;
	for(int i=1;i<=n;i++){
		ll tmp=lower_bound(c,c+cnt,s[i])-c+1;
		add(tmp);
	}
	for(int i=1;i<=n;i++){
		ll l=lower_bound(c,c+cnt,s[i-1])-c+1;
		ll r=lower_bound(c,c+cnt,s[i])-c+1;
		sum+=n-i+1-query(l-1);
		sub(r);
	}
	cout<<sum;
}

例题二:求区间和在[L,R]的区间个数

P5459 [BJOI2016]回转寿司

如果用BIT,和上面代码类似,这是我一开始的代码,过了。

const ll inf=1e18+7;
const int maxn=2e5+7; 
ll n,m,a[maxn],t[maxn<<2],sum,k,s[maxn],c[maxn<<2],cnt,l,r;
ll lowbit(ll i){	return i&(-i);	}
void add(ll i){	for(;i<=cnt;i+=lowbit(i))	t[i]++;	}
void sub(ll i){	for(;i<=cnt;i+=lowbit(i))	t[i]--;	}
ll query(ll i){
	int ans=0;
	for(;i;i-=lowbit(i))	ans+=t[i];
	return ans;
}
int main(){
	n=read();	l=read();	r=read();
	for(int i=1;i<=n;i++){
		a[i]=read();
		s[i]=s[i-1]+a[i];
		c[cnt++]=s[i];
		c[cnt++]=s[i]+l-1;
		c[cnt++]=s[i]+r;
	}
	c[cnt++]=inf;	c[cnt++]=0;	c[cnt++]=l-1;	c[cnt++]=r;
	sort(c,c+cnt);
	cnt=unique(c,c+cnt)-c;
	for(int i=1;i<=n;i++){
		ll tmp=lower_bound(c,c+cnt,s[i])-c+1;
		add(tmp);
	}
	for(int i=1;i<=n;i++){
		ll p=lower_bound(c,c+cnt,s[i-1]+l-1)-c+1;
		ll q=lower_bound(c,c+cnt,s[i-1]+r)-c+1;
		sum+=query(q)-query(p);
		ll tmp=lower_bound(c,c+cnt,s[i])-c+1;
		sub(tmp);
	}
	cout<<sum;
}

既然是刷cdq分治,补一下cdq分治的代码吧。

const int maxn=2e5+7;
ll n,p,q,s[maxn],t,sum;
void cdq(int l,int r){
	if(l==r)	return;
	int mid=l+r>>1;
	cdq(l,mid);	cdq(mid+1,r);
	int x=l,y=l-1;
	for(int i=mid+1;i<=r;i++){
		while(y+1<=mid&&s[i]>=s[y+1]+p)	y++;
		while(x<=mid&&s[i]>s[x]+q)	x++;
		sum+=y-x+1;
	}
	sort(s+l,s+r+1);
}
int main(){
	n=read();	p=read();	q=read();
	for(int i=1;i<=n;i++)	s[i]=s[i-1]+(t=read());
	cdq(0,n);
	cout<<sum;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值