【洛谷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;
}