description
你需要维护
n
n
n个可重整数集,开始都是空集
现在有
m
m
m次操作
1 l r k
在 [ l , r ] [l,r] [l,r]的集合中加入一个数 k k k2 l r k
询问 [ l , r ] [l,r] [l,r]的集合的并集中的第 k k k大,特别的,并集不去重
solution
看到动态区间第
k
k
k大,显然想到树套树
但是树状数组套权值线段树或者线段树套权值线段树都不能支持在外围的区间修改
所以我们可以考虑反过来
用权值树状数组套线段树
外围的树状数组表示每一个数值,里面的线段树表示这个数值在每个位置上出现的次数
那么这样的话
对于每一个
o
p
t
=
1
opt=1
opt=1,转化成了一个点的区间修改,复杂度
O
(
n
log
2
n
)
O(n\log^2n)
O(nlog2n)
对于每一个
o
p
t
=
2
opt=2
opt=2,我们先二分,然后判断是否可行,就可以得到答案,复杂度
O
(
n
log
3
n
)
O(n\log^3 n)
O(nlog3n)
空间复杂度的话,每次最多修改 log n \log n logn棵线段树,每棵线段树最多会多出 log n \log n logn个节点(注意线段树区间修改的修改点数也是log级别的),所以空间复杂度是 O ( n log 2 n ) O(n\log ^2 n) O(nlog2n)
虽然 n ≤ 5 × 1 0 4 n\leq 5\times 10^4 n≤5×104,但是这道题的值域是 2 n 2n 2n级别的,所以3个 log \log log的做法实际只能通过34分
我们考虑如何进行优化
我们为什么要选用权值树状数组而不是线段树呢,是因为他的常数更小
但是我们发现,在线段树上进行二分是2log的,而树状数组二分通常是3log的(虽然有2log做法但是我不会qwq),所以我们可以在外围的权值树状数组变成权值线段树
于是这题可能成为了权值线段树套线段树的唯一模板题…
code
码量也不是很大,树套树这个东西看着挺恶心其实通常还不是很难写
#include <bits/stdc++.h>
using namespace std;
# define Rep(i,a,b) for(int i=a;i<=b;i++)
# define _Rep(i,a,b) for(int i=a;i>=b;i--)
# define RepG(i,u) for(int i=head[u];~i;i=e[i].next)
typedef long long ll;
const int N=1e5+5;
template<typename T> void read(T &x){
x=0;int f=1;
char c=getchar();
for(;!isdigit(c);c=getchar())if(c=='-')f=-1;
for(;isdigit(c);c=getchar())x=(x<<1)+(x<<3)+c-'0';
x*=f;
}
int n,m;
int root[N<<2];
int tot;
struct node{
int lc,rc;
ll val,tag;
}seg[N*200];
int lowbit(int o){
return o&-o;
}
void update(int &u,int l,int r,int ql,int qr,int k){
if(!u)u=++tot;
seg[u].val+=1ll*(min(r,qr)-max(l,ql)+1)*k;
if(l>=ql&&r<=qr){
seg[u].tag+=k;
return;
}
int mid=l+r>>1;
if(ql<=mid)update(seg[u].lc,l,mid,ql,qr,k);
if(qr>mid)update(seg[u].rc,mid+1,r,ql,qr,k);
}
void add(int u,int l,int r,int x,int ql,int qr){
update(root[u],1,n,ql,qr,1);
if(l==r)return;
int mid=l+r>>1;
if(x<=mid)add(u<<1,l,mid,x,ql,qr);
else add(u<<1|1,mid+1,r,x,ql,qr);
}
ll query(int u,int l,int r,int ql,int qr,int tagsum){
if(l>=ql&&r<=qr)return seg[u].val+1ll*(r-l+1)*tagsum;
int mid=l+r>>1;
ll res=0;
if(ql<=mid)res+=query(seg[u].lc,l,mid,ql,qr,tagsum+seg[u].tag);
if(qr>mid)res+=query(seg[u].rc,mid+1,r,ql,qr,tagsum+seg[u].tag);
return res;
}
int kth(int u,int l,int r,int ql,int qr,ll k){
if(l==r)return l;
int mid=l+r>>1;
ll rank=query(root[u<<1|1],1,n,ql,qr,0);
if(k<=rank)return kth(u<<1|1,mid+1,r,ql,qr,k);
else return kth(u<<1,l,mid,ql,qr,k-rank);
}
int main()
{
read(n),read(m);
Rep(i,1,m){
int opt,l,r;
ll k;
read(opt),read(l),read(r),read(k);
if(opt==1)add(1,1,2*n+1,k+n+1,l,r);
else printf("%d\n",kth(1,1,2*n+1,l,r,k)-n-1);
}
return 0;
}