bzoj 3110 [Zjoi2013]K大数查询 整体二分

本文介绍了一种使用整体二分法解决特定类型查询问题的方法。通过将问题转化为二分搜索的形式,可以在固定的查询区间内高效地找到第C大的数值。文章详细解释了算法的实现思路,并提供了一段AC代码作为参考。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Description

有N个位置,M个操作。操作有两种,每次操作如果是1 a b c的形式表示在第a个位置到第b个位置,每个位置加入一个数c
如果是2 a b c形式,表示询问从第a个位置到第b个位置,第C大的数是多少。

Input

第一行N,M
接下来M行,每行形如1 a b c或2 a b c

Output

输出每个询问的结果


       大概是什么加上ZJOI这四个字母都变成刁钻题了吧...

       既然是整体二分题,先考虑如何二分呢,如果根据一般的套路,对于直接对于时间进行二分,我们并不能找到满足二分的性质,但是有二分性质的肯定是一个与时间有关的量,那么就是答案的数值了。

       由于询问的区间固定,那么如果把一部分的数字加进来,答案满足,那么加更多的数字一定满足,所以我们找到了可以二分的特性了。

       那么根据以上特性,在区间[l,r]对于加入操作每次把大于等于mid的数字加进来,对于查询操作看一看查询区间是否能够满足有k个数,如果已经满足了,那么答案[mid,r]这个区间递归得到答案,如果不满足说明答案在[l,mid-1]这个区间,就将当前这个查询的k减去已经构成的答案,再去递归求解即可得到答案。

       由于输入的数值有负数的如果直接进行二分常数略大,所以可以选择先离散化一下即可。

       下附AC代码

#include<iostream>
#include<stdio.h>
#include<string.h>
#include<algorithm>
#define mid ((nl+nr)>>1)
#define lson (now<<1)
#define rson ((now<<1)|1)
#define maxn 500005
using namespace std;
typedef long long ll;
struct nod
{
	ll f,l,r,val,pos,flag;
}q[maxn];
bool operator<(nod a,nod b)
{
	return a.flag==b.flag ? a.pos<b.pos : a.flag<b.flag;
}
ll sz;
ll n,m;
ll ans[maxn];
ll tot;
ll a[maxn],b[maxn];
ll id[maxn],temp[maxn];
ll sum[maxn<<2],lazy[maxn<<2],flag[maxn<<2];
void pushup(ll now)
{
	sum[now]=sum[lson]+sum[rson];
}
void pushdown(ll now,ll nl,ll nr)
{
	if(lazy[now])
	{
		sum[lson]+=lazy[now]*(mid-nl+1);
		sum[rson]+=lazy[now]*(nr-mid);
		lazy[lson]+=lazy[now];
		lazy[rson]+=lazy[now];
		lazy[now]=0;
	}
	return;
}
void update(ll ql,ll qr,ll add,ll nl,ll nr,ll now)
{
	if(nl>nr || ql>qr) return;
	if(ql<=nl && nr<=qr)
	{
		sum[now]+=add*(nr-nl+1);
		lazy[now]+=add;
		return;
	}
	pushdown(now,nl,nr);
	if(ql<=mid) update(ql,qr,add,nl,mid,lson);
	if(mid<qr)  update(ql,qr,add,mid+1,nr,rson);
	pushup(now);
}
ll query(ll ql,ll qr,ll nl,ll nr,ll now)
{
	if(nl>nr || ql>qr) return 0;
	if(ql<=nl && nr<=qr)
	{
		return sum[now];
	}
	pushdown(now,nl,nr);
	ll ans=0;
	if(ql<=mid) ans+=query(ql,qr,nl,mid,lson);
	if(mid<qr)  ans+=query(ql,qr,mid+1,nr,rson);
	pushup(now);
	return ans;
}
void dfs(ll nl,ll nr,ll ql,ll qr)
{
	if(ql>qr || nl>nr) return;
	if(nl==nr)
	{
		for(ll i=ql;i<=qr;i++)
		{
			ans[q[i].pos]=nl;
		}
		return;
	}
	
	ll l1=ql,cnt=0;
	for(ll i=ql;i<=qr;i++)
	{
		if(q[i].f==1)
		{
			if(q[i].val>b[mid])
			{
				update(q[i].l,q[i].r,1,1,n,1);
				q[i].flag=2;
			}
			else
			{
				cnt++;
				q[i].flag=1;
			}
		}
		else
		{
			ll now=query(q[i].l,q[i].r,1,n,1);
			if(now>=q[i].val)
			{
				q[i].flag=2;
			}
			else
			{
				cnt++;
				q[i].val-=now;
				q[i].flag=1;
			}
		}
	}
	
	for(ll i=ql;i<=qr;i++)
	{
		if(q[i].f==1)
		{
			if(q[i].val>b[mid])
			{
				update(q[i].l,q[i].r,-1,1,n,1);
			}
		}
	}
	sort(q+ql,q+qr+1);
	
	dfs(nl,mid,ql,ql+cnt-1);
	dfs(mid+1,nr,ql+cnt,qr);
}
int main()
{
	scanf("%lld%lld",&n,&m);
	for(ll i=1;i<=m;i++)
	{
		scanf("%lld%lld%lld%lld",&q[i].f,&q[i].l,&q[i].r,&q[i].val);
		q[i].pos=i;
		if(q[i].f==1)
		{
			b[++tot]=q[i].val;
		}
	}
	sort(b+1,b+1+tot);
	sz=unique(b+1,b+1+tot)-(b+1);

	memset(ans,-1,sizeof(ans));
	dfs(1,sz,1,m);
	for(ll i=1;i<=m;i++)
	q[i].flag=0;
	sort(q+1,q+1+m);
	for(ll i=1;i<=m;i++)
	if(q[i].f==2)
	{
		if(ans[i]==-1)
		printf("%lld\n",ans[i]);
		else
		printf("%lld\n",b[ans[i]]);
	}
}


       



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值