题目大意
有N个集合,初始为空。有M个操作,
修改操作:编号范围在l~r的集合都加入一个数值为a的数,
询问操作:编号范围在l~r的集合数值为第k大的数。
n,m<=50000,|a|<=n,k
树套树
当然可行,但我不会
考虑离线——整体二分
L,R表示数值的区间,mid=(L+R)/2。
用线段树维护在编号范围在l~r的集合中数值范围在mid+1~R的数的个数
修改操作的a的数值若>mid则编号范围在l~r(修改的范围)的数的个数+1,并把该操作传到mid+1~R否则传到L~mid的区间
询问操作直接得到编号范围在l~r的数的个数,若>k则把该操作传到mid+1~R的区间否则传到L~mid的区间
注意
每次线段树要清空,要打标记清空,否则会很慢。
代码
#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;
const int maxn=50010;
struct pc{
int id,l,r,c,p,b;
} a[maxn],a1[maxn],a2[maxn];
int tree[6*maxn],bz[6*maxn],ans[maxn],n;
bool b[6*maxn],bk[maxn];
void down(int k,int l,int r)
{ int m=(l+r)/2;
if (b[k]){
b[k*2]=b[k*2+1]=1;
b[k]=bz[k*2]=bz[k*2+1]=tree[k*2]=tree[k*2+1]=0;
}
if (bz[k]){
tree[k*2]+=bz[k]*(m-l+1);
tree[k*2+1]+=bz[k]*(r-m);
bz[k*2]+=bz[k];bz[k*2+1]+=bz[k];bz[k]=0;
}
}
void change(int k,int l,int r,int a,int b)
{
if (l==a&&r==b){
tree[k]+=r-l+1;
bz[k]++;
return;
}
down(k,l,r);
int m=(l+r)/2;
if (b<=m) change(k*2,l,m,a,b); else
if (a>m) change(k*2+1,m+1,r,a,b); else{
change(k*2,l,m,a,m);change(k*2+1,m+1,r,m+1,b);
}
tree[k]=tree[2*k]+tree[2*k+1];
}
int find(int k,int l,int r,int a,int b)
{ if (l==a&&r==b) return tree[k];
down(k,l,r);
int m=(l+r)/2;
if (b<=m) return find(k*2,l,m,a,b); else
if (a>m) return find(k*2+1,m+1,r,a,b); else
return find(k*2,l,m,a,m)+find(k*2+1,m+1,r,m+1,b);
}
void cdq(int x,int y,int l,int r)
{
if (x>y) return;
int i,m=(l+r+2*n)/2-n;
if (l==r){
for (i=x;i<=y;i++) ans[a[i].id]=l;
return;
}
b[1]=1;
bz[1]=0;tree[1]=0;
for (i=x;i<=y;i++){
if (a[i].b){
int j=find(1,1,n,a[i].l,a[i].r);a[i].p=1;
if (j<a[i].c) {
a[i].p=0;
a[i].c-=j;
}
continue;
}
if (a[i].c>m)
{ change(1,1,n,a[i].l,a[i].r);
a[i].p=1;
}
}int t=0;
for (i=x;i<=y;i++){
if (!a[i].p)a1[++t]=a[i];else a2[i-x+1-t]=a[i];
}
for (i=x;i<=y;i++) {
if (i-x+1<=t) a[i]=a1[i-x+1];else a[i]=a2[i-x+1-t];
a[i].p=0;
}
cdq(x,x+t-1,l,m);cdq(x+t,y,m+1,r);
}
int main(){
int m;
scanf("%d%d",&n,&m);
for (int i=1;i<=m;i++)
{ scanf("%d%d%d%d",&a[i].b,&a[i].l,&a[i].r,&a[i].c);a[i].id=i;a[i].p=0;
a[i].b--;bk[i]=a[i].b;
}
cdq(1,m,-n,n);
for (int i=1;i<=m;i++)if (bk[i]) printf("%d\n",ans[i]);
}