【洛谷P3332】K大数查询

题目

题目链接:https://www.luogu.com.cn/problem/P3332
你需要维护 \(n\) 个可重整数集,集合的编号从 \(1\)\(n\)
这些集合初始都是空集,有 \(m\) 个操作:

  • 1 l r c:表示将 \(c\) 加入到编号在 \([l,r]\) 内的集合中
  • 2 l r c:表示查询编号在 \([l,r]\) 内的集合的并集中,第 \(c\) 大的数是多少。

注意可重集的并是不去除重复元素的,如 \(\{1,1,4\}\cup\{5,1,4\}=\{1,1,4,5,1,4\}\)

思路

整体二分板子题。
我们将操作整体二分,假设答案在 \([l,r]\) 的询问操作和能对 \([l,r]\) 有贡献的修改操作所在集合为 \([ql,qr]\),那么我们依次枚举操作。如果该操作是询问操作,那么就在线段树上查询询问区间的和是否不小于 \(c\)。如果是,说明答案应该更大,否则应该更小。对于修改操作,如果修改的值不小于 \(mid\),那么我们就将它所对应区间全部加上 \(1\) 的贡献,然后将它放到 \([mid,r]\) 区间内,否则不计入贡献并放进 \([l,mid)\) 内。
时间复杂度 \(O((n+m)\log n)\)

代码

#include <bits/stdc++.h>
#define int long long
using namespace std;
typedef long long ll;

const int N=50010;
int n,m,Q,ans[N];

struct Query
{
	int opt,l,r,id;
	ll c;
}ask[N],cpy[N];

struct SegTree
{
	ll cnt[N*8],lazy[N*8];
	
	void pushdown(int x,int l,int r)
	{
		if (lazy[x])
		{
			int mid=(l+r)>>1;
			cnt[x*2]+=1LL*(mid-l+1)*lazy[x];
			cnt[x*2+1]+=1LL*(r-mid)*lazy[x];
			lazy[x*2]+=lazy[x];
			lazy[x*2+1]+=lazy[x];
			lazy[x]=0;
		}
	}
	
	void pushup(int x)
	{
		cnt[x]=cnt[x*2]+cnt[x*2+1];
	}
	
	void update(int x,int l,int r,int ql,int qr,ll val)
	{
		if (ql<=l && r<=qr)
		{
			cnt[x]+=(r-l+1)*val; lazy[x]+=val;
			return;
		}
		pushdown(x,l,r);
		int mid=(l+r)>>1;
		if (ql<=mid) update(x*2,l,mid,ql,qr,val);
		if (qr>mid) update(x*2+1,mid+1,r,ql,qr,val);
		pushup(x);
	}
	
	ll query(int x,int l,int r,int ql,int qr)
	{
		if (ql<=l && r<=qr) return cnt[x];
		pushdown(x,l,r);
		int mid=(l+r)>>1,sum=0;
		if (ql<=mid) sum+=query(x*2,l,mid,ql,qr);
		if (qr>mid) sum+=query(x*2+1,mid+1,r,ql,qr);
		return sum;
	}
}seg;

void binary(int l,int r,int ql,int qr)
{
	if (l==r)
	{
		for (int i=ql;i<=qr;i++)
			if (ask[i].opt==2) ans[ask[i].id]=l;
		return;
	}
	int mid=(l+r+1)>>1,p=ql-1,q=qr+1;
	for (int i=ql;i<=qr;i++)
		if (ask[i].opt==1)
		{
			if (ask[i].c>=mid)
			{
				seg.update(1,1,n*2+1,ask[i].l,ask[i].r,1);
				cpy[--q]=ask[i];
			}
			else cpy[++p]=ask[i];
		}
		else
		{
			int cnt=seg.query(1,1,n*2+1,ask[i].l,ask[i].r);
			if (1LL*cnt<ask[i].c) 
			{
				ask[i].c-=cnt; 
				cpy[++p]=ask[i];
			}
			else cpy[--q]=ask[i];
		}
	for (int i=ql;i<=p;i++) ask[i]=cpy[i];
	for (int i=qr;i>=q;i--) ask[i]=cpy[qr-i+q];
	for (int i=ql;i<=qr;i++)
		if (ask[i].opt==1 && ask[i].c>=mid)
			seg.update(1,1,n*2+1,ask[i].l,ask[i].r,-1);
	binary(l,mid-1,ql,p);
	binary(mid,r,q,qr);
}

signed main()
{
	scanf("%lld%lld",&n,&m);
	for (int i=1;i<=m;i++)
	{
		scanf("%lld%lld%lld",&ask[i].opt,&ask[i].l,&ask[i].r);
		scanf("%lld",&ask[i].c);
		if (ask[i].opt==1) ask[i].c+=n+1;
			else ask[i].id=++Q;
	}
	binary(1,2*n+1,1,m);
	for (int i=1;i<=Q;i++)
		printf("%lld\n",ans[i]-n-1);
	return 0;
}
posted @ 2021-01-04 21:03  stoorz  阅读(142)  评论(0编辑  收藏  举报