题目
题目链接:https://www.luogu.com.cn/problem/P5494
给出一个可重集 (a)(编号为 (1)),它支持以下操作:
0 p x y
:将可重集 (p) 中大于等于 (x) 且小于等于 (y) 的值放入一个新的可重集中(新可重集编号为从 (2) 开始的正整数,是上一次产生的新可重集的编号+1)。
1 p t
:将可重集 (t) 中的数放入可重集 (p),且清空可重集 (t)(数据保证在此后的操作中不会出现可重集 (t))。
2 p x q
:在 (p) 这个可重集中加入 (x) 个数字 (q)。
3 p x y
:查询可重集 (p) 中大于等于 (x) 且小于等于 (y) 的值的个数。
4 p k
:查询在 (p) 这个可重集中第 (k) 小的数,不存在时输出 -1
。
(n,mleq 2 imes 10^5)。
思路
操作 (1,2,3,4) 可以用权值线段树和线段树合并解决。至于操作 (1) 线段树合并的复杂度,每次可以看作是 (O( ext{节点数较小值})) 的,所以一般的线段树合并是 (O(nlog n))。在本题中,操作 (0,2) 单次加入的节点数均为 (O(log n)),所以复杂度没有问题。
考虑操作 (0),也就是把权值线段树的一个区间 ([l,r]) 分为 ([l,k]) 和 ([k+1,r]) 扔到两棵权值线段树上。类似 FHQ Treap 的 split,对于当前区间 ([l,r]),如果 (kleq mid),则左边的权值线段树的这一层节点为空,右边的直接设为 (x);反之同理。这样就保证了每次只会找 (O(log n)) 个节点。
而合并 ([0,x),(y,n]) 区间的时候,由于这两棵线段树没有交,单次复杂度也是 (O(log n)) 的。
时间复杂度 (O(mlog n))。
代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=200010,LG=18;
int n,m,Q,rt[N];
struct SegTree
{
int tot,lc[N*LG*4],rc[N*LG*4];
ll cnt[N*LG*4];
void pushup(int x)
{
cnt[x]=cnt[lc[x]]+cnt[rc[x]];
}
int update(int x,int l,int r,int k,ll v)
{
if (!x) x=++tot;
if (l==r) { cnt[x]+=v; return x; }
int mid=(l+r)>>1;
if (k<=mid) lc[x]=update(lc[x],l,mid,k,v);
else rc[x]=update(rc[x],mid+1,r,k,v);
pushup(x);
return x;
}
void split(int x,int l,int r,int v,int &p,int &q)
{
if (!x) return;
if (l==r) { p=x; q=0; return; }
int mid=(l+r)>>1;
if (v<=mid)
q=x,p=++tot,split(lc[x],l,mid,v,lc[p],lc[q]);
else
p=x,q=++tot,split(rc[x],mid+1,r,v,rc[p],rc[q]);
pushup(p); pushup(q);
}
int merge(int x,int y,int l,int r)
{
if (!x || !y) return x|y;
if (l==r) { cnt[x]+=cnt[y]; return x; }
int mid=(l+r)>>1;
lc[x]=merge(lc[x],lc[y],l,mid);
rc[x]=merge(rc[x],rc[y],mid+1,r);
pushup(x);
return x;
}
ll query1(int x,int l,int r,int ql,int qr)
{
if (!x) return 0;
if (ql<=l && qr>=r) return cnt[x];
int mid=(l+r)>>1; ll res=0;
if (ql<=mid) res+=query1(lc[x],l,mid,ql,qr);
if (qr>mid) res+=query1(rc[x],mid+1,r,ql,qr);
return res;
}
int query2(int x,int l,int r,ll k)
{
if (l==r) return l;
int mid=(l+r)>>1;
if (cnt[lc[x]]>=k) return query2(lc[x],l,mid,k);
else return query2(rc[x],mid+1,r,k-cnt[lc[x]]);
}
}seg;
int main()
{
// freopen("P5494_1.in","r",stdin);
// freopen("ans.out","w",stdout);
scanf("%d%d",&n,&Q);
for (int i=1,x;i<=n;i++)
{
scanf("%d",&x);
rt[1]=seg.update(rt[1],1,n,i,x);
}
m=1;
while (Q--)
{
int opt,x,y,z;
scanf("%d",&opt);
if (opt==0)
{
int p=0,q=0;
scanf("%d%d%d",&x,&y,&z);
if (y>1) seg.split(rt[x],1,n,y-1,p,rt[x]);
seg.split(rt[x],1,n,z,rt[++m],q);
rt[x]=seg.merge(p,q,1,n);
}
if (opt==1)
{
scanf("%d%d",&x,&y);
rt[x]=seg.merge(rt[x],rt[y],1,n);
}
if (opt==2)
{
scanf("%d%d%d",&x,&y,&z);
rt[x]=seg.update(rt[x],1,n,z,y);
}
if (opt==3)
{
scanf("%d%d%d",&x,&y,&z);
cout<<seg.query1(rt[x],1,n,y,z)<<"
";
}
if (opt==4)
{
ll k;
scanf("%d",&x); scanf("%lld",&k);
if (seg.cnt[rt[x]]<k) cout<<"-1
";
else cout<<seg.query2(rt[x],1,n,k)<<"
";
}
}
return 0;
}